From 3b9b6d0b8e7f798023c9d109c490449d528fde80 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:59:48 +0200 Subject: Adding upstream version 1:9.18.19. Signed-off-by: Daniel Baumann --- bin/Makefile.am | 1 + bin/Makefile.in | 734 + bin/check/Makefile.am | 34 + bin/check/Makefile.in | 872 + bin/check/check-tool.c | 693 + bin/check/check-tool.h | 52 + bin/check/named-checkconf.c | 754 + bin/check/named-checkconf.rst | 108 + bin/check/named-checkzone.c | 569 + bin/check/named-checkzone.rst | 222 + bin/check/named-compilezone.rst | 224 + bin/confgen/Makefile.am | 30 + bin/confgen/Makefile.in | 876 + bin/confgen/ddns-confgen.rst | 96 + bin/confgen/include/confgen/os.h | 33 + bin/confgen/keygen.c | 178 + bin/confgen/keygen.h | 41 + bin/confgen/os.c | 36 + bin/confgen/rndc-confgen.c | 294 + bin/confgen/rndc-confgen.rst | 121 + bin/confgen/tsig-keygen.c | 301 + bin/confgen/tsig-keygen.rst | 54 + bin/confgen/util.c | 49 + bin/confgen/util.h | 42 + bin/delv/Makefile.am | 21 + bin/delv/Makefile.in | 823 + bin/delv/delv.c | 1868 ++ bin/delv/delv.rst | 364 + bin/dig/Makefile.am | 39 + bin/dig/Makefile.in | 897 + bin/dig/dig.c | 3102 +++ bin/dig/dig.rst | 809 + bin/dig/dighost.c | 4989 +++++ bin/dig/dighost.h | 466 + bin/dig/host.c | 931 + bin/dig/host.rst | 193 + bin/dig/nslookup.c | 977 + bin/dig/nslookup.rst | 208 + bin/dig/readline.h | 58 + bin/dnssec/Makefile.am | 38 + bin/dnssec/Makefile.in | 976 + bin/dnssec/dnssec-cds.c | 1361 ++ bin/dnssec/dnssec-cds.rst | 221 + bin/dnssec/dnssec-dsfromkey.c | 561 + bin/dnssec/dnssec-dsfromkey.rst | 159 + bin/dnssec/dnssec-importkey.c | 477 + bin/dnssec/dnssec-importkey.rst | 142 + bin/dnssec/dnssec-keyfromlabel.c | 760 + bin/dnssec/dnssec-keyfromlabel.rst | 289 + bin/dnssec/dnssec-keygen.c | 1292 ++ bin/dnssec/dnssec-keygen.rst | 357 + bin/dnssec/dnssec-revoke.c | 263 + bin/dnssec/dnssec-revoke.rst | 78 + bin/dnssec/dnssec-settime.c | 967 + bin/dnssec/dnssec-settime.rst | 271 + bin/dnssec/dnssec-signzone.c | 4165 ++++ bin/dnssec/dnssec-signzone.rst | 436 + bin/dnssec/dnssec-verify.c | 345 + bin/dnssec/dnssec-verify.rst | 107 + bin/dnssec/dnssectool.c | 585 + bin/dnssec/dnssectool.h | 104 + bin/named/Makefile.am | 123 + bin/named/Makefile.in | 964 + bin/named/bind9.xsl | 1106 ++ bin/named/builtin.c | 650 + bin/named/config.c | 1034 + bin/named/control.c | 307 + bin/named/controlconf.c | 1494 ++ bin/named/dlz_dlopen_driver.c | 552 + bin/named/fuzz.c | 782 + bin/named/geoip.c | 147 + bin/named/include/dlz/dlz_dlopen_driver.h | 20 + bin/named/include/named/builtin.h | 24 + bin/named/include/named/config.h | 82 + bin/named/include/named/control.h | 108 + bin/named/include/named/fuzz.h | 22 + bin/named/include/named/geoip.h | 28 + bin/named/include/named/globals.h | 163 + bin/named/include/named/log.h | 84 + bin/named/include/named/logconf.h | 25 + bin/named/include/named/main.h | 36 + bin/named/include/named/os.h | 75 + bin/named/include/named/server.h | 396 + bin/named/include/named/smf_globals.h | 38 + bin/named/include/named/statschannel.h | 51 + bin/named/include/named/tkeyconf.h | 43 + bin/named/include/named/transportconf.h | 43 + bin/named/include/named/tsigconf.h | 41 + bin/named/include/named/types.h | 38 + bin/named/include/named/zoneconf.h | 76 + bin/named/log.c | 253 + bin/named/logconf.c | 374 + bin/named/main.c | 1663 ++ bin/named/named.conf.rst | 67 + bin/named/named.rst | 254 + bin/named/os.c | 932 + bin/named/server.c | 16755 ++++++++++++++++ bin/named/statschannel.c | 4084 ++++ bin/named/tkeyconf.c | 115 + bin/named/transportconf.c | 263 + bin/named/tsigconf.c | 181 + bin/named/xsl_p.h | 16 + bin/named/zoneconf.c | 2114 ++ bin/nsupdate/Makefile.am | 34 + bin/nsupdate/Makefile.in | 828 + bin/nsupdate/nsupdate.c | 3463 ++++ bin/nsupdate/nsupdate.rst | 391 + bin/plugins/Makefile.am | 15 + bin/plugins/Makefile.in | 847 + bin/plugins/filter-a.c | 879 + bin/plugins/filter-a.rst | 86 + bin/plugins/filter-aaaa.c | 881 + bin/plugins/filter-aaaa.rst | 90 + bin/rndc/Makefile.am | 26 + bin/rndc/Makefile.in | 831 + bin/rndc/rndc.c | 1125 ++ bin/rndc/rndc.conf.rst | 158 + bin/rndc/rndc.rst | 667 + bin/rndc/util.c | 49 + bin/rndc/util.h | 42 + bin/tests/Makefile.am | 38 + bin/tests/Makefile.in | 977 + bin/tests/convert-trs-to-junit.py | 148 + bin/tests/system/Makefile.am | 258 + bin/tests/system/Makefile.in | 2416 +++ bin/tests/system/README | 843 + bin/tests/system/acl/clean.sh | 26 + bin/tests/system/acl/ns2/named1.conf.in | 62 + bin/tests/system/acl/ns2/named2.conf.in | 66 + bin/tests/system/acl/ns2/named3.conf.in | 75 + bin/tests/system/acl/ns2/named4.conf.in | 74 + bin/tests/system/acl/ns2/named5.conf.in | 64 + bin/tests/system/acl/ns3/example.db | 21 + bin/tests/system/acl/ns3/named.conf.in | 36 + bin/tests/system/acl/ns4/example.db | 21 + bin/tests/system/acl/ns4/existing.db | 21 + bin/tests/system/acl/ns4/named.conf.in | 41 + bin/tests/system/acl/setup.sh | 21 + bin/tests/system/acl/tests.sh | 230 + bin/tests/system/acl/tests_sh_acl.py | 14 + bin/tests/system/additional/clean.sh | 23 + bin/tests/system/additional/ns1/mx.db | 18 + bin/tests/system/additional/ns1/named.args | 2 + bin/tests/system/additional/ns1/named1.conf.in | 62 + bin/tests/system/additional/ns1/named2.conf.in | 62 + bin/tests/system/additional/ns1/named3.conf.in | 63 + bin/tests/system/additional/ns1/named4.conf.in | 72 + bin/tests/system/additional/ns1/naptr.db | 20 + bin/tests/system/additional/ns1/naptr2.db | 20 + bin/tests/system/additional/ns1/nid.db | 21 + bin/tests/system/additional/ns1/root.db | 21 + bin/tests/system/additional/ns1/rt.db | 21 + bin/tests/system/additional/ns1/rt2.db | 20 + bin/tests/system/additional/ns1/srv.db | 18 + bin/tests/system/additional/ns2/named.conf.in | 31 + bin/tests/system/additional/ns2/root.db | 21 + bin/tests/system/additional/ns3/ex.db | 16 + bin/tests/system/additional/ns3/ex2.db | 15 + bin/tests/system/additional/ns3/named.conf.in | 42 + bin/tests/system/additional/ns3/root.hint | 13 + bin/tests/system/additional/setup.sh | 19 + bin/tests/system/additional/tests.sh | 379 + bin/tests/system/additional/tests_sh_additional.py | 14 + bin/tests/system/addzone/clean.sh | 44 + bin/tests/system/addzone/ns1/inlinesec.db | 26 + bin/tests/system/addzone/ns1/named.conf.in | 47 + bin/tests/system/addzone/ns1/redirect.db.1 | 13 + bin/tests/system/addzone/ns1/redirect.db.2 | 13 + bin/tests/system/addzone/ns2/added.db | 26 + bin/tests/system/addzone/ns2/default.nzf.in | 14 + bin/tests/system/addzone/ns2/hints.db | 14 + bin/tests/system/addzone/ns2/inline.db | 26 + bin/tests/system/addzone/ns2/named1.conf.in | 44 + bin/tests/system/addzone/ns2/named2.conf.in | 68 + bin/tests/system/addzone/ns2/named3.conf.in | 78 + bin/tests/system/addzone/ns2/normal.db | 26 + bin/tests/system/addzone/ns2/previous.db | 26 + bin/tests/system/addzone/ns2/redirect.db.1 | 13 + bin/tests/system/addzone/ns2/redirect.db.2 | 13 + bin/tests/system/addzone/ns3/e.db | 14 + bin/tests/system/addzone/ns3/example.db | 13 + bin/tests/system/addzone/ns3/named1.conf.in | 38 + bin/tests/system/addzone/ns3/named2.conf.in | 29 + bin/tests/system/addzone/ns3/redirect.db.1 | 14 + bin/tests/system/addzone/ns3/redirect.db.2 | 14 + bin/tests/system/addzone/setup.sh | 25 + bin/tests/system/addzone/tests.sh | 756 + bin/tests/system/addzone/tests_rndc_deadlock.py | 92 + bin/tests/system/addzone/tests_sh_addzone.py | 14 + bin/tests/system/allow-query/clean.sh | 24 + bin/tests/system/allow-query/ns1/named.conf.in | 26 + bin/tests/system/allow-query/ns1/root.db | 18 + bin/tests/system/allow-query/ns2/generic.db | 33 + bin/tests/system/allow-query/ns2/named01.conf.in | 33 + bin/tests/system/allow-query/ns2/named02.conf.in | 34 + bin/tests/system/allow-query/ns2/named03.conf.in | 34 + bin/tests/system/allow-query/ns2/named04.conf.in | 34 + bin/tests/system/allow-query/ns2/named05.conf.in | 34 + bin/tests/system/allow-query/ns2/named06.conf.in | 34 + bin/tests/system/allow-query/ns2/named07.conf.in | 36 + bin/tests/system/allow-query/ns2/named08.conf.in | 36 + bin/tests/system/allow-query/ns2/named09.conf.in | 36 + bin/tests/system/allow-query/ns2/named10.conf.in | 39 + bin/tests/system/allow-query/ns2/named11.conf.in | 45 + bin/tests/system/allow-query/ns2/named12.conf.in | 39 + bin/tests/system/allow-query/ns2/named21.conf.in | 36 + bin/tests/system/allow-query/ns2/named22.conf.in | 39 + bin/tests/system/allow-query/ns2/named23.conf.in | 38 + bin/tests/system/allow-query/ns2/named24.conf.in | 38 + bin/tests/system/allow-query/ns2/named25.conf.in | 38 + bin/tests/system/allow-query/ns2/named26.conf.in | 38 + bin/tests/system/allow-query/ns2/named27.conf.in | 41 + bin/tests/system/allow-query/ns2/named28.conf.in | 40 + bin/tests/system/allow-query/ns2/named29.conf.in | 40 + bin/tests/system/allow-query/ns2/named30.conf.in | 43 + bin/tests/system/allow-query/ns2/named31.conf.in | 50 + bin/tests/system/allow-query/ns2/named32.conf.in | 43 + bin/tests/system/allow-query/ns2/named33.conf.in | 40 + bin/tests/system/allow-query/ns2/named34.conf.in | 39 + bin/tests/system/allow-query/ns2/named40.conf.in | 108 + bin/tests/system/allow-query/ns2/named53.conf.in | 35 + bin/tests/system/allow-query/ns2/named54.conf.in | 35 + bin/tests/system/allow-query/ns2/named55.conf.in | 40 + bin/tests/system/allow-query/ns2/named56.conf.in | 39 + bin/tests/system/allow-query/ns2/named57.conf.in | 43 + bin/tests/system/allow-query/ns3/named.args | 2 + bin/tests/system/allow-query/ns3/named1.conf.in | 35 + bin/tests/system/allow-query/ns3/named2.conf.in | 38 + bin/tests/system/allow-query/ns3/named3.conf.in | 38 + bin/tests/system/allow-query/ns3/named4.conf.in | 38 + bin/tests/system/allow-query/setup.sh | 19 + bin/tests/system/allow-query/tests.sh | 739 + .../system/allow-query/tests_sh_allowquery.py | 14 + bin/tests/system/ans.pl | 534 + bin/tests/system/auth/clean.sh | 20 + bin/tests/system/auth/ns1/chaos.db | 23 + bin/tests/system/auth/ns1/example.com.db | 25 + bin/tests/system/auth/ns1/example.net.db | 22 + bin/tests/system/auth/ns1/named.conf.in | 44 + bin/tests/system/auth/ns2/named.conf.in | 37 + bin/tests/system/auth/setup.sh | 17 + bin/tests/system/auth/tests.sh | 192 + bin/tests/system/auth/tests_sh_auth.py | 14 + bin/tests/system/autosign/clean.sh | 75 + bin/tests/system/autosign/ns1/keygen.sh | 53 + bin/tests/system/autosign/ns1/named.conf.in | 48 + bin/tests/system/autosign/ns1/root.db.in | 26 + bin/tests/system/autosign/ns2/Xbar.+013+59973.key | 5 + .../system/autosign/ns2/Xbar.+013+59973.private | 6 + bin/tests/system/autosign/ns2/Xbar.+013+60101.key | 5 + .../system/autosign/ns2/Xbar.+013+60101.private | 6 + bin/tests/system/autosign/ns2/bar.db.in | 80 + .../system/autosign/ns2/child.nsec3.example.db | 20 + .../system/autosign/ns2/child.optout.example.db | 20 + bin/tests/system/autosign/ns2/dst.example.db.in | 21 + bin/tests/system/autosign/ns2/example.db.in | 88 + .../system/autosign/ns2/insecure.secure.example.db | 26 + bin/tests/system/autosign/ns2/keygen.sh | 65 + bin/tests/system/autosign/ns2/named.conf.in | 109 + .../system/autosign/ns2/optout-with-ent.db.in | 22 + .../autosign/ns2/private.secure.example.db.in | 27 + .../system/autosign/ns3/autonsec3.example.db.in | 37 + .../autosign/ns3/cdnskey-delete.example.db.in | 28 + .../system/autosign/ns3/cds-delete.example.db.in | 28 + bin/tests/system/autosign/ns3/delay.example.db | 26 + bin/tests/system/autosign/ns3/delzsk.example.db.in | 25 + .../autosign/ns3/dname-at-apex-nsec3.example.db.in | 16 + .../system/autosign/ns3/inacksk2.example.db.in | 26 + .../system/autosign/ns3/inacksk3.example.db.in | 26 + .../system/autosign/ns3/inaczsk.example.db.in | 26 + .../system/autosign/ns3/inaczsk2.example.db.in | 26 + .../system/autosign/ns3/inaczsk3.example.db.in | 26 + bin/tests/system/autosign/ns3/insecure.example.db | 26 + .../system/autosign/ns3/jitter.nsec3.example.db.in | 22 + bin/tests/system/autosign/ns3/keygen.sh | 398 + .../system/autosign/ns3/kskonly.example.db.in | 34 + bin/tests/system/autosign/ns3/named.conf.in | 335 + bin/tests/system/autosign/ns3/noksk.example.db.in | 26 + bin/tests/system/autosign/ns3/nozsk.example.db.in | 26 + .../system/autosign/ns3/nsec-only.example.db.in | 26 + .../autosign/ns3/nsec3-to-nsec.example.db.in | 26 + bin/tests/system/autosign/ns3/nsec3.example.db.in | 37 + .../system/autosign/ns3/nsec3.nsec3.example.db.in | 35 + .../system/autosign/ns3/nsec3.optout.example.db.in | 35 + .../system/autosign/ns3/oldsigs.example.db.in | 26 + bin/tests/system/autosign/ns3/optout.example.db.in | 38 + .../system/autosign/ns3/optout.nsec3.example.db.in | 35 + .../autosign/ns3/optout.optout.example.db.in | 35 + .../system/autosign/ns3/rsasha256.example.db.in | 28 + .../system/autosign/ns3/rsasha512.example.db.in | 28 + .../autosign/ns3/secure-to-insecure.example.db.in | 26 + .../autosign/ns3/secure-to-insecure2.example.db.in | 26 + bin/tests/system/autosign/ns3/secure.example.db.in | 37 + .../system/autosign/ns3/secure.nsec3.example.db.in | 35 + .../autosign/ns3/secure.optout.example.db.in | 35 + bin/tests/system/autosign/ns3/sync.example.db.in | 34 + bin/tests/system/autosign/ns3/ttl1.example.db.in | 26 + bin/tests/system/autosign/ns3/ttl2.example.db.in | 26 + bin/tests/system/autosign/ns3/ttl3.example.db.in | 26 + bin/tests/system/autosign/ns3/ttl4.example.db.in | 26 + bin/tests/system/autosign/ns4/named.conf.in | 35 + bin/tests/system/autosign/ns5/named.conf.in | 33 + bin/tests/system/autosign/setup.sh | 23 + bin/tests/system/autosign/tests.sh | 1800 ++ bin/tests/system/autosign/tests_sh_autosign.py | 14 + bin/tests/system/builtin/clean.sh | 20 + bin/tests/system/builtin/ns1/named.conf.in | 31 + bin/tests/system/builtin/ns2/named.conf.in | 32 + bin/tests/system/builtin/ns3/named.conf.in | 34 + bin/tests/system/builtin/setup.sh | 18 + bin/tests/system/builtin/tests.sh | 248 + bin/tests/system/builtin/tests_sh_builtin.py | 14 + bin/tests/system/cacheclean/clean.sh | 27 + bin/tests/system/cacheclean/dig.batch | 924 + bin/tests/system/cacheclean/knowngood.dig.out | 953 + bin/tests/system/cacheclean/ns1/example.db | 2942 +++ bin/tests/system/cacheclean/ns1/expire-test.db | 21 + bin/tests/system/cacheclean/ns1/flushtest.db | 44 + bin/tests/system/cacheclean/ns1/named.args | 1 + bin/tests/system/cacheclean/ns1/named.conf.in | 42 + bin/tests/system/cacheclean/ns2/named.args | 1 + bin/tests/system/cacheclean/ns2/named.conf.in | 50 + bin/tests/system/cacheclean/setup.sh | 17 + bin/tests/system/cacheclean/tests.sh | 269 + bin/tests/system/cacheclean/tests_sh_cacheclean.py | 14 + bin/tests/system/case/clean.sh | 24 + bin/tests/system/case/dynamic.good | 6 + bin/tests/system/case/ns1/dynamic.db.in | 26 + bin/tests/system/case/ns1/example.db | 23 + bin/tests/system/case/ns1/named.conf.in | 41 + bin/tests/system/case/ns2/named.conf.in | 41 + bin/tests/system/case/postns1.good | 6 + bin/tests/system/case/postupdate.good | 6 + bin/tests/system/case/setup.sh | 18 + bin/tests/system/case/tests.sh | 151 + bin/tests/system/case/tests_sh_case.py | 14 + bin/tests/system/catz/clean.sh | 37 + bin/tests/system/catz/ns1/catalog-bad1.example.db | 13 + bin/tests/system/catz/ns1/catalog-bad2.example.db | 14 + bin/tests/system/catz/ns1/catalog-bad3.example.db | 15 + bin/tests/system/catz/ns1/catalog-bad4.example.db | 14 + bin/tests/system/catz/ns1/catalog-bad5.example.db | 14 + bin/tests/system/catz/ns1/catalog.example.db.in | 14 + bin/tests/system/catz/ns1/named.conf.in | 144 + bin/tests/system/catz/ns2/dom-existing.example.db | 13 + bin/tests/system/catz/ns2/named1.conf.in | 184 + bin/tests/system/catz/ns2/named2.conf.in | 127 + bin/tests/system/catz/ns3/catalog.example.db.in | 14 + bin/tests/system/catz/ns3/dom5.example.db | 13 + bin/tests/system/catz/ns3/dom6.example.db | 13 + bin/tests/system/catz/ns3/named.conf.in | 58 + bin/tests/system/catz/ns4/catalog.example.db.in | 14 + bin/tests/system/catz/ns4/named.conf.in | 66 + bin/tests/system/catz/setup.sh | 30 + bin/tests/system/catz/tests.sh | 2658 +++ bin/tests/system/catz/tests_sh_catz.py | 14 + bin/tests/system/cds/checkmtime.pl | 18 + bin/tests/system/cds/checktime.pl | 27 + bin/tests/system/cds/clean.sh | 23 + bin/tests/system/cds/mangle.pl | 19 + bin/tests/system/cds/setup.sh | 138 + bin/tests/system/cds/tests.sh | 260 + bin/tests/system/cds/tests_sh_cds.py | 14 + bin/tests/system/chain/README | 22 + bin/tests/system/chain/ans3/ans.pl | 131 + bin/tests/system/chain/ans4/README.anspy | 24 + bin/tests/system/chain/ans4/ans.py | 386 + bin/tests/system/chain/clean.sh | 18 + bin/tests/system/chain/ns1/named.conf.in | 27 + bin/tests/system/chain/ns1/root.db | 51 + bin/tests/system/chain/ns2/example.db | 69 + bin/tests/system/chain/ns2/generic.db | 22 + bin/tests/system/chain/ns2/named.conf.in | 74 + bin/tests/system/chain/ns2/sign.sh | 54 + bin/tests/system/chain/ns2/sub.db | 26 + bin/tests/system/chain/ns2/wildcard-secure.db | 29 + bin/tests/system/chain/ns2/wildcard.db | 28 + bin/tests/system/chain/ns5/named.conf.in | 42 + bin/tests/system/chain/ns5/sub.db | 26 + bin/tests/system/chain/ns7/named.conf.in | 45 + bin/tests/system/chain/ns7/root.hint | 14 + bin/tests/system/chain/setup.sh | 22 + bin/tests/system/chain/tests.sh | 630 + bin/tests/system/chain/tests_sh_chain.py | 14 + bin/tests/system/checkconf/altdb.conf | 19 + bin/tests/system/checkconf/altdlz.conf | 27 + bin/tests/system/checkconf/ancient.conf | 19 + bin/tests/system/checkconf/bad-acl.conf | 21 + bin/tests/system/checkconf/bad-also-notify.conf | 22 + bin/tests/system/checkconf/bad-catz-zone-dup.conf | 21 + .../checkconf/bad-catz-zone-primary-dup.conf | 27 + bin/tests/system/checkconf/bad-catz-zone.conf | 18 + .../checkconf/bad-checknames-primary-dup-2.conf | 17 + .../checkconf/bad-checknames-primary-dup.conf | 17 + .../checkconf/bad-checknames-secondary-dup.conf | 17 + .../system/checkconf/bad-dnskey-validity.conf | 16 + bin/tests/system/checkconf/bad-dnssec.conf | 31 + bin/tests/system/checkconf/bad-doh-1.conf | 26 + bin/tests/system/checkconf/bad-doh-2.conf | 22 + bin/tests/system/checkconf/bad-doh-3.conf | 21 + bin/tests/system/checkconf/bad-doh-badpath-1.conf | 21 + bin/tests/system/checkconf/bad-doh-badpath-2.conf | 21 + bin/tests/system/checkconf/bad-doh-badpath-3.conf | 21 + bin/tests/system/checkconf/bad-doh-default.conf | 20 + bin/tests/system/checkconf/bad-doh-duplicates.conf | 40 + bin/tests/system/checkconf/bad-dot-1.conf | 17 + .../checkconf/bad-dot-allow-transfer-bad-port.conf | 18 + .../bad-dot-allow-transfer-bad-proto-1.conf | 18 + .../bad-dot-allow-transfer-bad-proto-2.conf | 18 + .../bad-dot-allow-transfer-bad-proto-3.conf | 18 + .../bad-dot-allow-transfer-bad-proto-4.conf | 18 + bin/tests/system/checkconf/bad-dot-badciphers.conf | 22 + .../system/checkconf/bad-dot-badprotocol.conf | 22 + .../system/checkconf/bad-dot-duplicatetls.conf | 26 + bin/tests/system/checkconf/bad-dot-ephemeral.conf | 22 + bin/tests/system/checkconf/bad-dot-nocert.conf | 32 + bin/tests/system/checkconf/bad-dot-nokey.conf | 32 + bin/tests/system/checkconf/bad-dot-none.conf | 22 + bin/tests/system/checkconf/bad-dot-primaries.conf | 19 + bin/tests/system/checkconf/bad-duplicate-key.conf | 36 + .../checkconf/bad-duplicate-primaries-1.conf | 15 + .../checkconf/bad-duplicate-primaries-2.conf | 15 + .../system/checkconf/bad-duplicate-root-key.conf | 36 + bin/tests/system/checkconf/bad-duration.conf | 16 + .../system/checkconf/bad-glue-cache-bogus.conf | 16 + bin/tests/system/checkconf/bad-hint.conf | 18 + bin/tests/system/checkconf/bad-in-view-dup.conf | 21 + bin/tests/system/checkconf/bad-inline-options.conf | 24 + .../system/checkconf/bad-inline-secondary.conf | 22 + bin/tests/system/checkconf/bad-inline-view.conf | 31 + .../system/checkconf/bad-interface-interval.conf | 16 + .../system/checkconf/bad-ipv4-prefix-dotted1.conf | 16 + .../system/checkconf/bad-ipv4-prefix-dotted2.conf | 16 + bin/tests/system/checkconf/bad-ipv4-prefix2.conf | 16 + .../system/checkconf/bad-kasp-define-default.conf | 23 + .../system/checkconf/bad-kasp-define-insecure.conf | 23 + .../system/checkconf/bad-kasp-define-none.conf | 23 + bin/tests/system/checkconf/bad-kasp-duplicate.conf | 15 + bin/tests/system/checkconf/bad-kasp-key1.conf | 24 + bin/tests/system/checkconf/bad-kasp-key2.conf | 24 + bin/tests/system/checkconf/bad-kasp-key3.conf | 24 + bin/tests/system/checkconf/bad-kasp-key4.conf | 24 + .../system/checkconf/bad-kasp-keydir1.conf.in | 50 + .../system/checkconf/bad-kasp-keydir2.conf.in | 48 + .../system/checkconf/bad-kasp-keydir3.conf.in | 55 + .../system/checkconf/bad-kasp-keydir4.conf.in | 52 + .../system/checkconf/bad-kasp-keydir5.conf.in | 52 + bin/tests/system/checkconf/bad-kasp-nsec3-alg.conf | 25 + .../bad-kasp-policy-undefined-inherited-view.conf | 25 + .../bad-kasp-policy-undefined-inherited.conf | 25 + bin/tests/system/checkconf/bad-kasp10.conf | 28 + bin/tests/system/checkconf/bad-kasp11.conf | 28 + bin/tests/system/checkconf/bad-kasp12.conf | 30 + bin/tests/system/checkconf/bad-kasp13.conf | 28 + bin/tests/system/checkconf/bad-kasp2.conf | 24 + bin/tests/system/checkconf/bad-kasp3.conf | 24 + bin/tests/system/checkconf/bad-kasp4.conf | 25 + bin/tests/system/checkconf/bad-kasp6.conf | 27 + bin/tests/system/checkconf/bad-kasp7.conf | 28 + bin/tests/system/checkconf/bad-kasp8.conf | 28 + bin/tests/system/checkconf/bad-kasp9.conf | 28 + .../system/checkconf/bad-keep-response-order.conf | 18 + .../system/checkconf/bad-ksk-without-zsk.conf | 24 + bin/tests/system/checkconf/bad-lifetime.conf | 16 + .../system/checkconf/bad-lmdb-mapsize-bogus.conf | 16 + .../checkconf/bad-lmdb-mapsize-toolarge.conf | 16 + .../checkconf/bad-lmdb-mapsize-toosmall.conf | 16 + .../checkconf/bad-lmdb-mapsize-unlimited.conf | 16 + .../system/checkconf/bad-master-request-ixfr.conf | 22 + .../checkconf/bad-masterfile-format-map.conf | 22 + bin/tests/system/checkconf/bad-maxcachettl.conf | 16 + bin/tests/system/checkconf/bad-maxncachettl-1.conf | 16 + bin/tests/system/checkconf/bad-maxncachettl-2.conf | 16 + bin/tests/system/checkconf/bad-maxncachettl-3.conf | 19 + bin/tests/system/checkconf/bad-maxncachettl-4.conf | 16 + bin/tests/system/checkconf/bad-maxratio1.conf | 19 + bin/tests/system/checkconf/bad-maxratio2.conf | 19 + bin/tests/system/checkconf/bad-mincachettl.conf | 16 + bin/tests/system/checkconf/bad-minncachettl.conf | 16 + .../checkconf/bad-mirror-allow-recursion-none.conf | 22 + .../checkconf/bad-mirror-explicit-notify-yes.conf | 17 + .../bad-mirror-non-root-zone-without-masters.conf | 16 + .../system/checkconf/bad-mirror-recursion-no.conf | 20 + .../system/checkconf/bad-mirror-zonename.conf | 17 + bin/tests/system/checkconf/bad-noddns.conf | 19 + .../system/checkconf/bad-notify-source-v6.conf | 22 + bin/tests/system/checkconf/bad-notify-source.conf | 22 + .../system/checkconf/bad-options-also-notify.conf | 21 + .../checkconf/bad-parental-agents-def-options.conf | 21 + .../checkconf/bad-parental-agents-def-view.conf | 20 + .../checkconf/bad-parental-agents-def-view2.conf | 22 + .../checkconf/bad-parental-agents-def-zone.conf | 18 + .../system/checkconf/bad-parental-agents-dup.conf | 19 + .../checkconf/bad-parental-agents-dupdef.conf | 26 + .../checkconf/bad-parental-agents-empty.conf | 20 + .../checkconf/bad-parental-agents-empty2.conf | 18 + .../checkconf/bad-parental-agents-mirror.conf | 18 + .../checkconf/bad-parental-agents-notfound.conf | 22 + .../system/checkconf/bad-parental-source-v6.conf | 22 + .../system/checkconf/bad-parental-source.conf | 22 + bin/tests/system/checkconf/bad-port.conf | 16 + bin/tests/system/checkconf/bad-primaries-dup.conf | 18 + bin/tests/system/checkconf/bad-primaries-key.conf | 17 + .../system/checkconf/bad-primaries-notfound.conf | 21 + bin/tests/system/checkconf/bad-primaries-tls.conf | 17 + bin/tests/system/checkconf/bad-printtime.conf | 19 + bin/tests/system/checkconf/bad-rate-limit-acl.conf | 20 + .../checkconf/bad-rate-limit-all-per-second.conf | 18 + .../bad-rate-limit-errors-per-second.conf | 18 + .../bad-rate-limit-ipv4-prefix-length.conf | 18 + .../bad-rate-limit-ipv6-prefix-length.conf | 18 + .../checkconf/bad-rate-limit-max-table-size.conf | 18 + .../bad-rate-limit-nodata-per-second.conf | 18 + .../bad-rate-limit-nxdomains-per-second.conf | 18 + .../system/checkconf/bad-rate-limit-qps-scale.conf | 18 + .../bad-rate-limit-referrals-per-second.conf | 18 + .../bad-rate-limit-responses-per-second.conf | 18 + .../system/checkconf/bad-rate-limit-slip.conf | 18 + .../system/checkconf/bad-rate-limit-window.conf | 18 + bin/tests/system/checkconf/bad-root-mixed-key.conf | 41 + .../system/checkconf/bad-rpz-too-many-zones.conf | 148 + bin/tests/system/checkconf/bad-rpz-ttl.conf | 24 + bin/tests/system/checkconf/bad-rpz-update.conf | 25 + bin/tests/system/checkconf/bad-rpz-zone.conf | 18 + .../system/checkconf/bad-sharedwritable1.conf | 22 + .../system/checkconf/bad-sharedwritable2.conf | 23 + bin/tests/system/checkconf/bad-sharedzone1.conf | 31 + bin/tests/system/checkconf/bad-sharedzone2.conf | 33 + bin/tests/system/checkconf/bad-sharedzone3.conf | 25 + bin/tests/system/checkconf/bad-sig-validity.conf | 16 + .../system/checkconf/bad-static-initial-1.conf | 17 + .../system/checkconf/bad-static-initial-2.conf | 17 + .../system/checkconf/bad-static-initial-3.conf | 17 + .../system/checkconf/bad-static-initial-4.conf | 17 + .../system/checkconf/bad-stub-masters-dialup.conf | 36 + .../system/checkconf/bad-transfer-source-v6.conf | 22 + .../system/checkconf/bad-transfer-source.conf | 22 + bin/tests/system/checkconf/bad-tsig.conf | 19 + bin/tests/system/checkconf/bad-unpaired-keys.conf | 27 + bin/tests/system/checkconf/bad-update-policy1.conf | 20 + .../system/checkconf/bad-update-policy10.conf | 20 + .../system/checkconf/bad-update-policy11.conf | 20 + .../system/checkconf/bad-update-policy12.conf | 20 + .../system/checkconf/bad-update-policy13.conf | 20 + .../system/checkconf/bad-update-policy14.conf | 20 + .../system/checkconf/bad-update-policy15.conf | 20 + .../system/checkconf/bad-update-policy16.conf | 20 + .../system/checkconf/bad-update-policy17.conf | 20 + .../system/checkconf/bad-update-policy18.conf | 20 + .../system/checkconf/bad-update-policy19.conf | 20 + bin/tests/system/checkconf/bad-update-policy2.conf | 20 + .../system/checkconf/bad-update-policy20.conf | 20 + bin/tests/system/checkconf/bad-update-policy3.conf | 20 + bin/tests/system/checkconf/bad-update-policy4.conf | 20 + bin/tests/system/checkconf/bad-update-policy5.conf | 20 + bin/tests/system/checkconf/bad-update-policy6.conf | 20 + bin/tests/system/checkconf/bad-update-policy7.conf | 20 + bin/tests/system/checkconf/bad-update-policy8.conf | 20 + bin/tests/system/checkconf/bad-update-policy9.conf | 20 + .../system/checkconf/bad-validation-auto-key.conf | 26 + .../system/checkconf/bad-view-also-notify.conf | 20 + .../system/checkconf/bad-zsk-without-ksk.conf | 24 + .../system/checkconf/check-dup-records-fail.conf | 23 + bin/tests/system/checkconf/check-dup-records.db | 33 + bin/tests/system/checkconf/check-missing-zone.conf | 26 + bin/tests/system/checkconf/check-mixed-keys.conf | 43 + .../system/checkconf/check-mx-cname-fail.conf | 22 + bin/tests/system/checkconf/check-mx-cname.db | 26 + bin/tests/system/checkconf/check-mx-fail.conf | 22 + bin/tests/system/checkconf/check-mx.db | 24 + bin/tests/system/checkconf/check-names-fail.conf | 22 + bin/tests/system/checkconf/check-names.db | 28 + .../system/checkconf/check-root-ksk-2010.conf | 26 + .../system/checkconf/check-root-ksk-2017.conf | 29 + .../system/checkconf/check-root-ksk-both.conf | 41 + .../system/checkconf/check-root-static-ds.conf | 16 + .../system/checkconf/check-root-static-key.conf | 29 + .../system/checkconf/check-root-trusted-key.conf | 29 + .../system/checkconf/check-srv-cname-fail.conf | 22 + bin/tests/system/checkconf/check-srv-cname.db | 28 + bin/tests/system/checkconf/check-wildcard-no.conf | 18 + bin/tests/system/checkconf/check-wildcard.conf | 18 + bin/tests/system/checkconf/check-wildcard.db | 23 + bin/tests/system/checkconf/clean.sh | 26 + bin/tests/system/checkconf/deprecated.conf | 66 + bin/tests/system/checkconf/dlz-bad.conf | 27 + bin/tests/system/checkconf/dnssec.1 | 27 + bin/tests/system/checkconf/dnssec.2 | 34 + bin/tests/system/checkconf/dnssec.3 | 18 + bin/tests/system/checkconf/good-acl.conf | 21 + .../good-allow-update-forwarding-view.conf | 16 + .../checkconf/good-allow-update-forwarding.conf | 16 + .../system/checkconf/good-allow-update-view.conf | 16 + bin/tests/system/checkconf/good-allow-update.conf | 16 + bin/tests/system/checkconf/good-class.conf | 14 + .../checkconf/good-dnskey-validity-3660.conf | 16 + .../checkconf/good-dnskey-validity-zero.conf | 16 + bin/tests/system/checkconf/good-doh-1.conf | 33 + bin/tests/system/checkconf/good-doh-2.conf | 29 + bin/tests/system/checkconf/good-doh-3.conf | 19 + bin/tests/system/checkconf/good-doh-4.conf | 31 + bin/tests/system/checkconf/good-doh-tlsopts.conf | 38 + bin/tests/system/checkconf/good-dot-1.conf | 21 + .../good-dot-allow-transfer-encrypted.conf | 49 + .../checkconf/good-dot-doh-tls-nokeycert.conf | 18 + .../checkconf/good-dot-primaries-ephemeral.conf | 19 + bin/tests/system/checkconf/good-dot-primaries.conf | 25 + bin/tests/system/checkconf/good-dot-tlsopts.conf | 26 + bin/tests/system/checkconf/good-ds-key-1.conf | 17 + bin/tests/system/checkconf/good-ds-key-2.conf | 17 + .../system/checkconf/good-dup-managed-key.conf | 33 + .../system/checkconf/good-dup-trusted-key.conf | 33 + bin/tests/system/checkconf/good-glue-cache.conf | 16 + bin/tests/system/checkconf/good-initial-ds.conf | 16 + .../system/checkconf/good-interface-interval.conf | 16 + bin/tests/system/checkconf/good-kasp.conf | 68 + bin/tests/system/checkconf/good-key-directory.conf | 73 + .../checkconf/good-masterfile-format-raw.conf | 22 + .../checkconf/good-masterfile-format-text.conf | 22 + .../checkconf/good-masters-and-primaries.conf | 15 + bin/tests/system/checkconf/good-maxcachettl.conf | 34 + bin/tests/system/checkconf/good-maxncachettl.conf | 34 + bin/tests/system/checkconf/good-maxratio1.conf | 19 + bin/tests/system/checkconf/good-maxratio2.conf | 19 + bin/tests/system/checkconf/good-mincachettl.conf | 28 + bin/tests/system/checkconf/good-minncachettl.conf | 28 + .../good-mirror-inherited-notify-yes.conf | 20 + .../good-mirror-root-zone-without-masters.conf | 16 + bin/tests/system/checkconf/good-nested.conf | 20 + .../system/checkconf/good-notify-source-v6.conf | 22 + bin/tests/system/checkconf/good-notify-source.conf | 22 + .../system/checkconf/good-options-also-notify.conf | 22 + .../system/checkconf/good-parental-source-v6.conf | 22 + .../system/checkconf/good-parental-source.conf | 22 + bin/tests/system/checkconf/good-printtime.conf | 35 + bin/tests/system/checkconf/good-response-dot.conf | 23 + bin/tests/system/checkconf/good-rpz-ttl.conf | 24 + bin/tests/system/checkconf/good-rpz-update.conf | 25 + .../system/checkconf/good-rrset-order-none.conf | 18 + .../checkconf/good-server-christmas-tree.conf.in | 61 + .../system/checkconf/good-sig-signing-type.conf | 18 + bin/tests/system/checkconf/good-static-ds.conf | 16 + .../system/checkconf/good-transfer-source-v6.conf | 22 + .../system/checkconf/good-transfer-source.conf | 22 + .../system/checkconf/good-update-policy1.conf | 20 + .../system/checkconf/good-update-policy10.conf | 20 + .../system/checkconf/good-update-policy11.conf | 20 + .../system/checkconf/good-update-policy12.conf | 20 + .../system/checkconf/good-update-policy13.conf | 20 + .../system/checkconf/good-update-policy2.conf | 20 + .../system/checkconf/good-update-policy3.conf | 20 + .../system/checkconf/good-update-policy4.conf | 20 + .../system/checkconf/good-update-policy5.conf | 20 + .../system/checkconf/good-update-policy6.conf | 20 + .../system/checkconf/good-update-policy7.conf | 20 + .../system/checkconf/good-update-policy8.conf | 20 + .../system/checkconf/good-update-policy9.conf | 20 + .../system/checkconf/good-view-also-notify.conf | 21 + bin/tests/system/checkconf/good.conf | 286 + bin/tests/system/checkconf/good.zonelist | 24 + bin/tests/system/checkconf/hint-nofile.conf | 17 + bin/tests/system/checkconf/in-view-good.conf | 25 + bin/tests/system/checkconf/inline-bad.conf | 27 + bin/tests/system/checkconf/inline-good.conf | 28 + bin/tests/system/checkconf/inline-no.conf | 27 + .../checkconf/kasp-and-other-dnssec-options.conf | 28 + bin/tests/system/checkconf/kasp-bad-keylen.conf | 24 + bin/tests/system/checkconf/kasp-bad-lifetime.conf | 91 + bin/tests/system/checkconf/kasp-bad-nsec3-alg.conf | 26 + .../system/checkconf/kasp-bad-nsec3-iter.conf | 61 + .../system/checkconf/kasp-bad-nsec3-salt.conf | 23 + .../checkconf/kasp-bad-signatures-refresh.conf | 46 + bin/tests/system/checkconf/kasp-ignore-keylen.conf | 27 + bin/tests/system/checkconf/kasp-warning.conf | 62 + .../system/checkconf/lmdb-mapsize-largest.conf | 16 + .../system/checkconf/lmdb-mapsize-smallest.conf | 16 + .../system/checkconf/max-cache-size-good.conf | 16 + bin/tests/system/checkconf/max-ttl.conf | 34 + bin/tests/system/checkconf/maxttl-bad.conf | 24 + bin/tests/system/checkconf/maxttl-bad.db | 25 + bin/tests/system/checkconf/maxttl.db | 25 + bin/tests/system/checkconf/notify.conf | 84 + bin/tests/system/checkconf/portrange-good.conf | 22 + bin/tests/system/checkconf/range.conf | 16 + .../checkconf/servestale.stale-refresh-time.0.conf | 16 + .../servestale.stale-refresh-time.29.conf | 16 + bin/tests/system/checkconf/setup.sh | 20 + bin/tests/system/checkconf/shared.example.db | 13 + bin/tests/system/checkconf/tests.sh | 664 + bin/tests/system/checkconf/tests_sh_checkconf.py | 14 + bin/tests/system/checkconf/view-class-any1.conf | 14 + bin/tests/system/checkconf/view-class-any2.conf | 14 + bin/tests/system/checkconf/view-class-in1.conf | 14 + bin/tests/system/checkconf/view-class-in2.conf | 14 + .../system/checkconf/warn-kasp-max-zone-ttl.conf | 27 + bin/tests/system/checkconf/warn-keydir.conf | 25 + bin/tests/system/checkconf/warn-maxratio1.conf | 19 + bin/tests/system/checkconf/warn-notify-source.conf | 22 + .../system/checkconf/warn-parental-source.conf | 22 + bin/tests/system/checkconf/warn-random-device.conf | 16 + .../system/checkconf/warn-transfer-source.conf | 22 + bin/tests/system/checkds/README | 26 + bin/tests/system/checkds/clean.sh | 27 + bin/tests/system/checkds/ns1/named.conf.in | 32 + bin/tests/system/checkds/ns1/root.db.in | 24 + bin/tests/system/checkds/ns1/setup.sh | 46 + bin/tests/system/checkds/ns10/named.conf.in | 32 + bin/tests/system/checkds/ns10/root.db.in | 24 + bin/tests/system/checkds/ns2/named.conf.in | 46 + bin/tests/system/checkds/ns2/setup.sh | 36 + bin/tests/system/checkds/ns2/template.db.in | 39 + bin/tests/system/checkds/ns3/named.conf.in | 41 + bin/tests/system/checkds/ns4/named.conf.in | 42 + bin/tests/system/checkds/ns5/named.conf.in | 46 + bin/tests/system/checkds/ns5/setup.sh | 26 + bin/tests/system/checkds/ns5/template.db.in | 40 + bin/tests/system/checkds/ns6/named.conf.in | 46 + bin/tests/system/checkds/ns7/named.conf.in | 47 + bin/tests/system/checkds/ns8/named.conf.in | 41 + bin/tests/system/checkds/ns8/root.hint | 14 + bin/tests/system/checkds/ns9/named.conf.in | 242 + bin/tests/system/checkds/ns9/setup.sh | 63 + bin/tests/system/checkds/ns9/template.db.in | 27 + bin/tests/system/checkds/setup.sh | 48 + bin/tests/system/checkds/tests_checkds.py | 470 + bin/tests/system/checknames/clean.sh | 27 + bin/tests/system/checknames/ns1/fail.example.db.in | 17 + bin/tests/system/checknames/ns1/fail.update.db.in | 16 + .../system/checknames/ns1/ignore.example.db.in | 18 + .../system/checknames/ns1/ignore.update.db.in | 16 + bin/tests/system/checknames/ns1/named.conf.in | 70 + bin/tests/system/checknames/ns1/root.db | 30 + bin/tests/system/checknames/ns1/warn.example.db.in | 17 + bin/tests/system/checknames/ns1/warn.update.db.in | 16 + bin/tests/system/checknames/ns2/named.conf.in | 31 + bin/tests/system/checknames/ns2/root.hints | 14 + bin/tests/system/checknames/ns3/named.conf.in | 31 + bin/tests/system/checknames/ns3/root.hints | 14 + bin/tests/system/checknames/ns4/named.conf.in | 44 + .../checknames/ns4/primary-ignore.update.db.in | 18 + bin/tests/system/checknames/ns4/root.hints | 14 + .../checknames/ns5/master-ignore.update.db.in | 18 + bin/tests/system/checknames/ns5/named.conf.in | 44 + bin/tests/system/checknames/ns5/root.hints | 14 + bin/tests/system/checknames/setup.sh | 34 + bin/tests/system/checknames/tests.sh | 192 + bin/tests/system/checknames/tests_sh_checknames.py | 14 + bin/tests/system/checkzone/clean.sh | 17 + bin/tests/system/checkzone/setup.sh | 21 + bin/tests/system/checkzone/tests.sh | 214 + bin/tests/system/checkzone/tests_sh_checkzone.py | 14 + bin/tests/system/checkzone/zones/.gitattributes | 1 + bin/tests/system/checkzone/zones/bad-badclass.raw | Bin 0 -> 104 bytes bin/tests/system/checkzone/zones/bad-caa-rr.db | Bin 0 -> 601 bytes bin/tests/system/checkzone/zones/bad-cdnskey.db | 15 + bin/tests/system/checkzone/zones/bad-cds.db | 15 + bin/tests/system/checkzone/zones/bad-dhcid.db | 13 + .../system/checkzone/zones/bad-dns-sd-reverse.db | 21 + bin/tests/system/checkzone/zones/bad-ds.db | 15 + bin/tests/system/checkzone/zones/bad-eid.db | 13 + .../system/checkzone/zones/bad-generate-garbage.db | 17 + .../checkzone/zones/bad-generate-missing-brace.db | 17 + .../system/checkzone/zones/bad-generate-range.db | 18 + .../system/checkzone/zones/bad-generate-tkey.db | 17 + bin/tests/system/checkzone/zones/bad-nimloc.db | 10 + bin/tests/system/checkzone/zones/bad-nsap-empty.db | 18 + .../system/checkzone/zones/bad-nsap-odd-nibble.db | 18 + .../system/checkzone/zones/bad-nsec3-padded.db | 21 + .../checkzone/zones/bad-nsec3owner-padded.db | 19 + .../system/checkzone/zones/bad-svcb-mandatory.db | 17 + .../system/checkzone/zones/bad-svcb-servername.db | 17 + bin/tests/system/checkzone/zones/bad-svcb.db | 17 + bin/tests/system/checkzone/zones/bad-tkey.db | 17 + bin/tests/system/checkzone/zones/bad-tsig.db.in | 17 + bin/tests/system/checkzone/zones/bad-unspec.db | 16 + bin/tests/system/checkzone/zones/bad1.db | Bin 0 -> 508 bytes bin/tests/system/checkzone/zones/bad2.db | 19 + bin/tests/system/checkzone/zones/bad3.db | 19 + bin/tests/system/checkzone/zones/bad4.db | 19 + bin/tests/system/checkzone/zones/badttl.db | 19 + bin/tests/system/checkzone/zones/crashzone.db | 62 + .../zones/delegating-ns-address-below-dname.db | 24 + .../system/checkzone/zones/generate-overflow.db | 17 + bin/tests/system/checkzone/zones/good-cdnskey.db | 15 + .../system/checkzone/zones/good-cds-unsigned.db | 16 + bin/tests/system/checkzone/zones/good-cds.db | 15 + .../system/checkzone/zones/good-dns-sd-reverse.db | 23 + bin/tests/system/checkzone/zones/good-gc-msdcs.db | 16 + .../checkzone/zones/good-generate-modifier.db | 20 + bin/tests/system/checkzone/zones/good-nsap.db | 18 + .../system/checkzone/zones/good-nsec3-nopadhash.db | 19 + .../checkzone/zones/good-occulted-ns-by-dname.db | 22 + .../checkzone/zones/good-occulted-ns-by-ns.db | 22 + .../system/checkzone/zones/good-spf-exception.db | 18 + bin/tests/system/checkzone/zones/good-svcb.db | 27 + bin/tests/system/checkzone/zones/good1.db | 19 + bin/tests/system/checkzone/zones/inherit.db | 12 + .../checkzone/zones/nowarn.inherited.owner.db | 13 + .../checkzone/zones/ns-address-below-dname.db | 22 + bin/tests/system/checkzone/zones/spf.db | 18 + bin/tests/system/checkzone/zones/test1.db | 17 + bin/tests/system/checkzone/zones/test2.db | 18 + .../system/checkzone/zones/warn.inherit.origin.db | 14 + .../system/checkzone/zones/warn.inherited.owner.db | 13 + bin/tests/system/checkzone/zones/zone1.db | 21 + bin/tests/system/ckdnsrps.sh | 167 + bin/tests/system/cleanall.sh | 36 + bin/tests/system/common/controls.conf.in | 22 + bin/tests/system/common/rndc.conf | 21 + bin/tests/system/common/rndc.key | 15 + bin/tests/system/common/root.hint | 14 + bin/tests/system/conf.sh.common | 738 + bin/tests/system/conf.sh.in | 130 + bin/tests/system/conftest.py | 639 + bin/tests/system/cookie/ans9/ans.py | 308 + bin/tests/system/cookie/bad-cookie-badaes.conf | 17 + bin/tests/system/cookie/bad-cookie-badhex.conf | 16 + .../system/cookie/bad-cookie-badsiphash24.conf | 17 + bin/tests/system/cookie/bad-cookie-toolong.conf | 16 + bin/tests/system/cookie/clean.sh | 24 + bin/tests/system/cookie/good-cookie-aes.conf | 17 + bin/tests/system/cookie/good-cookie-siphash24.conf | 17 + bin/tests/system/cookie/ns1/example.db | 24 + bin/tests/system/cookie/ns1/named.conf.in | 60 + bin/tests/system/cookie/ns1/root.hint | 14 + bin/tests/system/cookie/ns2/named.conf.in | 31 + bin/tests/system/cookie/ns2/root.db | 28 + bin/tests/system/cookie/ns3/named.conf.in | 52 + bin/tests/system/cookie/ns3/root.hint | 14 + bin/tests/system/cookie/ns4/named.conf.in | 41 + bin/tests/system/cookie/ns4/root.hint | 14 + bin/tests/system/cookie/ns5/named.conf.in | 42 + bin/tests/system/cookie/ns5/root.hint | 14 + bin/tests/system/cookie/ns6/named.conf.in | 41 + bin/tests/system/cookie/ns6/root.hint | 14 + bin/tests/system/cookie/ns7/named.conf.in | 32 + bin/tests/system/cookie/ns7/root.db | 24 + bin/tests/system/cookie/ns8/example.db | 13 + bin/tests/system/cookie/ns8/named.conf.in | 39 + bin/tests/system/cookie/setup.sh | 23 + bin/tests/system/cookie/tests.sh | 550 + bin/tests/system/cookie/tests_sh_cookie.py | 14 + bin/tests/system/custom-test-driver | 162 + bin/tests/system/database/clean.sh | 17 + bin/tests/system/database/ns1/named1.conf.in | 41 + bin/tests/system/database/ns1/named2.conf.in | 41 + bin/tests/system/database/setup.sh | 16 + bin/tests/system/database/tests.sh | 56 + bin/tests/system/database/tests_sh_database.py | 14 + bin/tests/system/dialup/clean.sh | 18 + bin/tests/system/dialup/ns1/example.db | 19 + bin/tests/system/dialup/ns1/named.conf.in | 40 + bin/tests/system/dialup/ns1/root.db | 20 + bin/tests/system/dialup/ns2/hint.db | 13 + bin/tests/system/dialup/ns2/named.conf.in | 40 + bin/tests/system/dialup/ns3/hint.db | 13 + bin/tests/system/dialup/ns3/named.conf.in | 40 + bin/tests/system/dialup/setup.sh | 19 + bin/tests/system/dialup/tests.sh | 66 + bin/tests/system/dialup/tests_sh_dialup.py | 14 + bin/tests/system/digcomp.pl | 164 + bin/tests/system/digdelv/ans4/startme | 1 + bin/tests/system/digdelv/ans5/ans.pl | 176 + bin/tests/system/digdelv/ans6/ans.pl | 84 + bin/tests/system/digdelv/ans7/ans.pl | 68 + bin/tests/system/digdelv/ans8/ans.py | 202 + bin/tests/system/digdelv/clean.sh | 35 + bin/tests/system/digdelv/ns1/named.conf.in | 30 + bin/tests/system/digdelv/ns1/root.db | 26 + bin/tests/system/digdelv/ns2/example.db.in | 51 + bin/tests/system/digdelv/ns2/named.conf.in | 34 + bin/tests/system/digdelv/ns2/sign.sh | 29 + bin/tests/system/digdelv/ns3/named.conf.in | 28 + bin/tests/system/digdelv/setup.sh | 23 + bin/tests/system/digdelv/tests.sh | 1403 ++ bin/tests/system/digdelv/tests_sh_digdelv.py | 14 + bin/tests/system/digdelv/yamlget.py | 35 + bin/tests/system/dispatch/ans3/ans.py | 99 + bin/tests/system/dispatch/clean.sh | 16 + bin/tests/system/dispatch/ns1/named.conf.in | 45 + bin/tests/system/dispatch/ns1/root.db | 16 + bin/tests/system/dispatch/ns2/example.db | 17 + bin/tests/system/dispatch/ns2/named.conf.in | 50 + bin/tests/system/dispatch/setup.sh | 17 + bin/tests/system/dispatch/tests_connreset.py | 27 + bin/tests/system/ditch.pl | 87 + bin/tests/system/dlzexternal/clean.sh | 26 + bin/tests/system/dlzexternal/driver/Makefile.am | 13 + bin/tests/system/dlzexternal/driver/Makefile.in | 778 + bin/tests/system/dlzexternal/driver/driver.c | 862 + bin/tests/system/dlzexternal/driver/driver.h | 35 + bin/tests/system/dlzexternal/ns1/named.conf.in | 85 + bin/tests/system/dlzexternal/ns1/root.db | 26 + bin/tests/system/dlzexternal/prereq.sh | 21 + bin/tests/system/dlzexternal/setup.sh | 18 + bin/tests/system/dlzexternal/tests.sh | 231 + .../system/dlzexternal/tests_sh_dlzexternal.py | 14 + bin/tests/system/dns64/clean.sh | 22 + bin/tests/system/dns64/conf/bad1.conf | 16 + bin/tests/system/dns64/conf/bad10.conf | 16 + bin/tests/system/dns64/conf/bad11.conf | 16 + bin/tests/system/dns64/conf/bad12.conf | 16 + bin/tests/system/dns64/conf/bad13.conf | 16 + bin/tests/system/dns64/conf/bad14.conf | 16 + bin/tests/system/dns64/conf/bad15.conf | 16 + bin/tests/system/dns64/conf/bad16.conf | 16 + bin/tests/system/dns64/conf/bad17.conf | 16 + bin/tests/system/dns64/conf/bad18.conf | 16 + bin/tests/system/dns64/conf/bad19.conf | 16 + bin/tests/system/dns64/conf/bad2.conf | 16 + bin/tests/system/dns64/conf/bad3.conf | 16 + bin/tests/system/dns64/conf/bad4.conf | 16 + bin/tests/system/dns64/conf/bad5.conf | 16 + bin/tests/system/dns64/conf/bad6.conf | 16 + bin/tests/system/dns64/conf/bad7.conf | 18 + bin/tests/system/dns64/conf/bad8.conf | 18 + bin/tests/system/dns64/conf/bad9.conf | 18 + bin/tests/system/dns64/conf/good1.conf | 22 + bin/tests/system/dns64/conf/good2.conf | 21 + bin/tests/system/dns64/conf/good3.conf | 21 + bin/tests/system/dns64/conf/good4.conf | 21 + bin/tests/system/dns64/conf/good5.conf | 18 + bin/tests/system/dns64/ns1/example.db | 56 + bin/tests/system/dns64/ns1/ipv4only.arpa.db | 15 + bin/tests/system/dns64/ns1/named.conf1.in | 63 + bin/tests/system/dns64/ns1/named.conf2.in | 66 + bin/tests/system/dns64/ns1/named.conf3.in | 55 + bin/tests/system/dns64/ns1/root.db | 19 + bin/tests/system/dns64/ns1/sign.sh | 25 + bin/tests/system/dns64/ns2/named.conf.in | 71 + bin/tests/system/dns64/ns2/rpz.db | 23 + bin/tests/system/dns64/setup.sh | 19 + bin/tests/system/dns64/tests.sh | 1456 ++ bin/tests/system/dns64/tests_sh_dns64.py | 14 + bin/tests/system/dnssec/README | 32 + bin/tests/system/dnssec/ans10/ans.py | 158 + bin/tests/system/dnssec/clean.sh | 117 + bin/tests/system/dnssec/dnssec_update_test.pl | 99 + bin/tests/system/dnssec/ns1/named.conf.in | 36 + bin/tests/system/dnssec/ns1/root.db.in | 37 + bin/tests/system/dnssec/ns1/sign.sh | 62 + bin/tests/system/dnssec/ns2/algroll.db.in | 26 + bin/tests/system/dnssec/ns2/badparam.db.in | 21 + .../system/dnssec/ns2/cdnskey-auto.secure.db.in | 14 + .../system/dnssec/ns2/cdnskey-kskonly.secure.db.in | 14 + .../system/dnssec/ns2/cdnskey-update.secure.db.in | 14 + bin/tests/system/dnssec/ns2/cdnskey.secure.db.in | 14 + bin/tests/system/dnssec/ns2/cds-auto.secure.db.in | 14 + .../system/dnssec/ns2/cds-kskonly.secure.db.in | 14 + .../system/dnssec/ns2/cds-update.secure.db.in | 14 + bin/tests/system/dnssec/ns2/cds.secure.db.in | 14 + bin/tests/system/dnssec/ns2/child.nsec3.example.db | 20 + .../system/dnssec/ns2/child.optout.example.db | 20 + bin/tests/system/dnssec/ns2/corp.db | 23 + bin/tests/system/dnssec/ns2/dst.example.db.in | 21 + bin/tests/system/dnssec/ns2/example.db.in | 171 + bin/tests/system/dnssec/ns2/hours-vs-days.db.in | 167 + bin/tests/system/dnssec/ns2/in-addr.arpa.db.in | 19 + .../system/dnssec/ns2/insecure.secure.example.db | 26 + bin/tests/system/dnssec/ns2/key.db.in | 45 + bin/tests/system/dnssec/ns2/named.conf.in | 205 + .../system/dnssec/ns2/private.secure.example.db.in | 28 + bin/tests/system/dnssec/ns2/rfc2335.example.db | 114 + bin/tests/system/dnssec/ns2/sign.sh | 332 + bin/tests/system/dnssec/ns2/single-nsec3.db.in | 21 + bin/tests/system/dnssec/ns2/template.secure.db.in | 14 + .../system/dnssec/ns2/too-many-iterations.db.in | 27 + .../system/dnssec/ns3/auto-nsec.example.db.in | 40 + .../system/dnssec/ns3/auto-nsec3.example.db.in | 40 + bin/tests/system/dnssec/ns3/bogus.example.db.in | 27 + .../dnssec/ns3/dname-at-apex-nsec3.example.db.in | 15 + .../dnssec/ns3/dnskey-nsec3-unknown.example.db.in | 29 + .../system/dnssec/ns3/dnskey-unknown.example.db.in | 29 + .../dnssec/ns3/dnskey-unsupported-2.example.db.in | 29 + .../dnssec/ns3/dnskey-unsupported.example.db.in | 29 + bin/tests/system/dnssec/ns3/dynamic.example.db.in | 25 + bin/tests/system/dnssec/ns3/expired.example.db.in | 44 + bin/tests/system/dnssec/ns3/expiring.example.db.in | 40 + bin/tests/system/dnssec/ns3/future.example.db.in | 40 + bin/tests/system/dnssec/ns3/generic.example.db.in | 23 + bin/tests/system/dnssec/ns3/inline.example.db | 26 + .../dnssec/ns3/insecure.below-cname.example.db | 26 + bin/tests/system/dnssec/ns3/insecure.example.db | 27 + .../system/dnssec/ns3/insecure.nsec3.example.db | 26 + .../system/dnssec/ns3/insecure.optout.example.db | 26 + bin/tests/system/dnssec/ns3/insecure2.example.db | 27 + bin/tests/system/dnssec/ns3/key.db.in | 26 + bin/tests/system/dnssec/ns3/kskonly.example.db.in | 26 + bin/tests/system/dnssec/ns3/lower.example.db.in | 21 + .../system/dnssec/ns3/managed-future.example.db.in | 40 + bin/tests/system/dnssec/ns3/multiple.example.db.in | 29 + bin/tests/system/dnssec/ns3/named.conf.in | 383 + bin/tests/system/dnssec/ns3/nosign.example.db.in | 23 + .../system/dnssec/ns3/nsec3-unknown.example.db.in | 29 + bin/tests/system/dnssec/ns3/nsec3.example.db.in | 38 + .../system/dnssec/ns3/nsec3.nsec3.example.db.in | 35 + .../system/dnssec/ns3/nsec3.optout.example.db.in | 35 + bin/tests/system/dnssec/ns3/occluded.example.db.in | 26 + .../system/dnssec/ns3/optout-unknown.example.db.in | 29 + bin/tests/system/dnssec/ns3/optout.example.db.in | 40 + .../system/dnssec/ns3/optout.nsec3.example.db.in | 35 + .../system/dnssec/ns3/optout.optout.example.db.in | 35 + .../dnssec/ns3/publish-inactive.example.db.in | 26 + .../system/dnssec/ns3/rsasha256.example.db.in | 28 + .../system/dnssec/ns3/rsasha512.example.db.in | 28 + .../dnssec/ns3/secure.below-cname.example.db.in | 26 + bin/tests/system/dnssec/ns3/secure.example.db.in | 49 + .../system/dnssec/ns3/secure.nsec3.example.db.in | 35 + .../system/dnssec/ns3/secure.optout.example.db.in | 35 + .../system/dnssec/ns3/siginterval.example.db.in | 21 + bin/tests/system/dnssec/ns3/siginterval1.conf | 21 + bin/tests/system/dnssec/ns3/siginterval2.conf | 21 + bin/tests/system/dnssec/ns3/sign.sh | 674 + .../system/dnssec/ns3/split-dnssec.example.db.in | 38 + .../system/dnssec/ns3/split-smart.example.db.in | 38 + bin/tests/system/dnssec/ns3/ttlpatch.example.db.in | 26 + .../system/dnssec/ns3/unsupported-algorithm.key | 1 + .../system/dnssec/ns3/update-nsec3.example.db.in | 40 + bin/tests/system/dnssec/ns3/upper.example.db.in | 21 + bin/tests/system/dnssec/ns4/managed-keys.bind.in | 21 + bin/tests/system/dnssec/ns4/named1.conf.in | 61 + bin/tests/system/dnssec/ns4/named2.conf.in | 42 + bin/tests/system/dnssec/ns4/named3.conf.in | 43 + bin/tests/system/dnssec/ns4/named4.conf.in | 78 + bin/tests/system/dnssec/ns4/named5.conf.in | 40 + bin/tests/system/dnssec/ns5/named1.conf.in | 43 + bin/tests/system/dnssec/ns5/named2.conf.in | 53 + bin/tests/system/dnssec/ns5/sign.sh | 39 + bin/tests/system/dnssec/ns6/named.args | 1 + bin/tests/system/dnssec/ns6/named.conf.in | 40 + bin/tests/system/dnssec/ns6/optout-tld.db.in | 22 + bin/tests/system/dnssec/ns6/sign.sh | 29 + bin/tests/system/dnssec/ns7/named.conf.in | 76 + bin/tests/system/dnssec/ns7/named.nosoa | 12 + .../system/dnssec/ns7/nosoa.secure.example.db | 22 + bin/tests/system/dnssec/ns7/sign.sh | 44 + bin/tests/system/dnssec/ns7/split-rrsig.db.in | 21 + bin/tests/system/dnssec/ns8/named.conf.in | 47 + bin/tests/system/dnssec/ns9/named.conf.in | 39 + bin/tests/system/dnssec/ntadiff.pl | 24 + bin/tests/system/dnssec/setup.sh | 52 + bin/tests/system/dnssec/signer/example.db.in | 17 + .../signer/general/Kexample.com.+008+15002.key | 5 + .../signer/general/Kexample.com.+008+15002.private | 13 + .../signer/general/Kexample.com.+008+63613.key | 5 + .../signer/general/Kexample.com.+008+63613.private | 13 + .../signer/general/Kexample.com.+010+18240.key | 5 + .../signer/general/Kexample.com.+010+18240.private | 13 + .../signer/general/Kexample.com.+010+28633.key | 5 + .../signer/general/Kexample.com.+010+28633.private | 13 + .../system/dnssec/signer/general/bogus-ksk.key | 6 + .../system/dnssec/signer/general/bogus-zsk.key | 6 + bin/tests/system/dnssec/signer/general/test1.zone | 19 + bin/tests/system/dnssec/signer/general/test2.zone | 18 + bin/tests/system/dnssec/signer/general/test3.zone | 18 + bin/tests/system/dnssec/signer/general/test4.zone | 20 + bin/tests/system/dnssec/signer/general/test5.zone | 19 + bin/tests/system/dnssec/signer/general/test6.zone | 21 + bin/tests/system/dnssec/signer/general/test7.zone | 19 + bin/tests/system/dnssec/signer/general/test8.zone | 19 + bin/tests/system/dnssec/signer/general/test9.zone | 19 + bin/tests/system/dnssec/signer/prepub.db.in | 17 + bin/tests/system/dnssec/signer/remove.db.in | 18 + bin/tests/system/dnssec/signer/remove2.db.in | 16 + bin/tests/system/dnssec/tests.sh | 4446 +++++ bin/tests/system/dnssec/tests_sh_dnssec.py | 14 + bin/tests/system/dnstap/README | 27 + .../system/dnstap/bad-fstrm-reopen-interval.conf | 16 + .../dnstap/bad-fstrm-set-buffer-hint-max.conf | 16 + .../dnstap/bad-fstrm-set-buffer-hint-min.conf | 16 + .../dnstap/bad-fstrm-set-flush-timeout-max.conf | 16 + .../dnstap/bad-fstrm-set-flush-timeout-min.conf | 16 + .../dnstap/bad-fstrm-set-input-queue-size-max.conf | 16 + .../dnstap/bad-fstrm-set-input-queue-size-min.conf | 16 + .../dnstap/bad-fstrm-set-input-queue-size-po2.conf | 16 + .../bad-fstrm-set-output-notify-threshold.conf | 16 + .../bad-fstrm-set-output-queue-size-max.conf | 19 + .../bad-fstrm-set-output-queue-size-min.conf | 16 + .../dnstap/bad-fstrm-set-reopen-interval-max.conf | 16 + .../dnstap/bad-fstrm-set-reopen-interval-min.conf | 16 + .../dnstap/bad-missing-dnstap-output-view.conf | 16 + .../system/dnstap/bad-missing-dnstap-output.conf | 17 + bin/tests/system/dnstap/bad-size-version.conf | 16 + bin/tests/system/dnstap/clean.sh | 30 + .../system/dnstap/good-dnstap-in-options.conf | 18 + bin/tests/system/dnstap/good-dnstap-in-view.conf | 21 + .../system/dnstap/good-fstrm-reopen-interval.conf | 16 + .../system/dnstap/good-fstrm-set-buffer-hint.conf | 16 + .../dnstap/good-fstrm-set-flush-timeout.conf | 16 + .../dnstap/good-fstrm-set-input-queue-size.conf | 16 + .../good-fstrm-set-output-notify-threshold.conf | 16 + .../good-fstrm-set-output-queue-model-mpsc.conf | 16 + .../good-fstrm-set-output-queue-model-spsc.conf | 16 + .../dnstap/good-fstrm-set-output-queue-size.conf | 16 + .../dnstap/good-fstrm-set-reopen-interval.conf | 16 + bin/tests/system/dnstap/good-size-unlimited.conf | 17 + bin/tests/system/dnstap/good-size-version.conf | 17 + bin/tests/system/dnstap/large-answer.fstrm | Bin 0 -> 981 bytes bin/tests/system/dnstap/ns1/named.conf.in | 47 + bin/tests/system/dnstap/ns1/root.db | 24 + bin/tests/system/dnstap/ns2/example.db.in | 30 + bin/tests/system/dnstap/ns2/named.conf.in | 53 + bin/tests/system/dnstap/ns3/named.args | 2 + bin/tests/system/dnstap/ns3/named.conf.in | 50 + bin/tests/system/dnstap/ns4/named.conf.in | 49 + bin/tests/system/dnstap/prereq.sh | 20 + bin/tests/system/dnstap/setup.sh | 21 + bin/tests/system/dnstap/tests.sh | 857 + bin/tests/system/dnstap/tests_dnstap.py | 83 + bin/tests/system/dnstap/tests_sh_dnstap.py | 14 + bin/tests/system/dnstap/ydump.py | 29 + bin/tests/system/doth/CA/CA.cfg | 121 + bin/tests/system/doth/CA/CA.pem | 29 + bin/tests/system/doth/CA/README | 2 + .../doth/CA/certs/srv01.client01.example.com.key | 6 + .../doth/CA/certs/srv01.client01.example.com.pem | 68 + .../CA/certs/srv01.client02-ns2.example.com.key | 6 + .../CA/certs/srv01.client02-ns2.example.com.pem | 68 + .../srv01.client03-ns2-expired.example.com.key | 6 + .../srv01.client03-ns2-expired.example.com.pem | 69 + .../doth/CA/certs/srv01.crt01.example.com.key | 6 + .../doth/CA/certs/srv01.crt01.example.com.pem | 69 + .../CA/certs/srv01.crt02-no-san.example.com.key | 6 + .../CA/certs/srv01.crt02-no-san.example.com.pem | 64 + .../CA/certs/srv01.crt03-expired.example.com.key | 6 + .../CA/certs/srv01.crt03-expired.example.com.pem | 69 + .../doth/CA/certs/srv02.crt01.example.com.key | 6 + .../doth/CA/certs/srv02.crt01.example.com.pem | 69 + .../doth/CA/certs/srv03.crt01.example.com.key | 6 + .../doth/CA/certs/srv03.crt01.example.com.pem | 69 + .../doth/CA/certs/srv04.crt01.example.com.key | 6 + .../doth/CA/certs/srv04.crt01.example.com.pem | 69 + bin/tests/system/doth/CA/index.txt | 9 + bin/tests/system/doth/CA/index.txt.attr | 1 + .../system/doth/CA/newcerts/6BB3183CDEF52001.pem | 69 + .../system/doth/CA/newcerts/6BB3183CDEF52002.pem | 64 + .../system/doth/CA/newcerts/6BB3183CDEF52003.pem | 69 + .../system/doth/CA/newcerts/6BB3183CDEF52004.pem | 69 + .../system/doth/CA/newcerts/6BB3183CDEF52005.pem | 69 + .../system/doth/CA/newcerts/6BB3183CDEF52006.pem | 69 + .../system/doth/CA/newcerts/6BB3183CDEF52007.pem | 68 + .../system/doth/CA/newcerts/6BB3183CDEF52008.pem | 68 + .../system/doth/CA/newcerts/6BB3183CDEF52009.pem | 69 + bin/tests/system/doth/CA/private/CA.key | 39 + bin/tests/system/doth/CA/serial | 1 + bin/tests/system/doth/README.curl | 38 + bin/tests/system/doth/clean.sh | 26 + bin/tests/system/doth/conftest.py | 47 + bin/tests/system/doth/dhparam3072.pem | 11 + bin/tests/system/doth/example.axfr.good | 2676 +++ bin/tests/system/doth/example8.axfr.good | 2676 +++ bin/tests/system/doth/get_openssl_version.py | 17 + bin/tests/system/doth/ns1/named.conf.in | 172 + bin/tests/system/doth/ns1/root.db | 10023 ++++++++++ bin/tests/system/doth/ns2/cert.pem | 14 + bin/tests/system/doth/ns2/key.pem | 8 + bin/tests/system/doth/ns2/named.conf.in | 183 + bin/tests/system/doth/ns3/named.conf.in | 79 + bin/tests/system/doth/ns4/named.conf.in | 73 + bin/tests/system/doth/prereq.sh | 20 + bin/tests/system/doth/setup.sh | 32 + bin/tests/system/doth/stress_http_quota.py | 252 + bin/tests/system/doth/tests.sh | 889 + bin/tests/system/doth/tests_gnutls.py | 108 + bin/tests/system/doth/tests_sh_doth.py | 14 + bin/tests/system/doth/tests_sslyze.py | 65 + bin/tests/system/dsdigest/clean.sh | 24 + bin/tests/system/dsdigest/ns1/named.conf.in | 36 + bin/tests/system/dsdigest/ns1/root.db.in | 26 + bin/tests/system/dsdigest/ns1/sign.sh | 36 + bin/tests/system/dsdigest/ns2/bad.db.in | 23 + bin/tests/system/dsdigest/ns2/good.db.in | 23 + bin/tests/system/dsdigest/ns2/named.conf.in | 46 + bin/tests/system/dsdigest/ns2/sign.sh | 43 + bin/tests/system/dsdigest/ns3/named.conf.in | 39 + bin/tests/system/dsdigest/ns4/named.conf.in | 37 + bin/tests/system/dsdigest/setup.sh | 21 + bin/tests/system/dsdigest/tests.sh | 55 + bin/tests/system/dsdigest/tests_sh_dsdigest.py | 14 + bin/tests/system/dupsigs/check_journal.pl | 206 + bin/tests/system/dupsigs/clean.sh | 24 + bin/tests/system/dupsigs/ns1/named.args | 1 + bin/tests/system/dupsigs/ns1/named.conf.in | 34 + bin/tests/system/dupsigs/ns1/reset_keys.sh | 99 + bin/tests/system/dupsigs/ns1/signing.test.db.in | 18 + bin/tests/system/dupsigs/setup.sh | 23 + bin/tests/system/dupsigs/tests.sh | 71 + bin/tests/system/dupsigs/tests_sh_dupsigs.py | 14 + bin/tests/system/dyndb/clean.sh | 25 + bin/tests/system/dyndb/driver/AUTHORS | 33 + bin/tests/system/dyndb/driver/Makefile.am | 25 + bin/tests/system/dyndb/driver/Makefile.in | 811 + bin/tests/system/dyndb/driver/README | 92 + bin/tests/system/dyndb/driver/db.c | 776 + bin/tests/system/dyndb/driver/db.h | 47 + bin/tests/system/dyndb/driver/driver.c | 164 + bin/tests/system/dyndb/driver/instance.c | 231 + bin/tests/system/dyndb/driver/instance.h | 73 + bin/tests/system/dyndb/driver/lock.c | 81 + bin/tests/system/dyndb/driver/lock.h | 39 + bin/tests/system/dyndb/driver/log.c | 44 + bin/tests/system/dyndb/driver/log.h | 45 + bin/tests/system/dyndb/driver/syncptr.c | 337 + bin/tests/system/dyndb/driver/syncptr.h | 46 + bin/tests/system/dyndb/driver/util.h | 82 + bin/tests/system/dyndb/driver/zone.c | 265 + bin/tests/system/dyndb/driver/zone.h | 45 + bin/tests/system/dyndb/ns1/named.conf.in | 40 + bin/tests/system/dyndb/prereq.sh | 21 + bin/tests/system/dyndb/setup.sh | 16 + bin/tests/system/dyndb/tests.sh | 166 + bin/tests/system/dyndb/tests_sh_dyndb.py | 14 + bin/tests/system/ecdsa/clean.sh | 28 + bin/tests/system/ecdsa/ns1/named.conf.in | 36 + bin/tests/system/ecdsa/ns1/root.db.in | 21 + bin/tests/system/ecdsa/ns1/sign.sh | 57 + bin/tests/system/ecdsa/ns2/named.conf.in | 36 + bin/tests/system/ecdsa/ns3/named.conf.in | 36 + bin/tests/system/ecdsa/setup.sh | 32 + bin/tests/system/ecdsa/tests.sh | 54 + bin/tests/system/ecdsa/tests_sh_ecdsa.py | 14 + bin/tests/system/eddsa/clean.sh | 29 + bin/tests/system/eddsa/ns1/named.conf.in | 36 + bin/tests/system/eddsa/ns1/root.db.in | 21 + bin/tests/system/eddsa/ns1/sign.sh | 57 + .../system/eddsa/ns2/Xexample.com.+015+03613.key | 1 + .../eddsa/ns2/Xexample.com.+015+03613.private | 4 + .../system/eddsa/ns2/Xexample.com.+015+35217.key | 1 + .../eddsa/ns2/Xexample.com.+015+35217.private | 3 + bin/tests/system/eddsa/ns2/example.com.db.in | 22 + bin/tests/system/eddsa/ns2/named.conf.in | 36 + bin/tests/system/eddsa/ns2/sign.sh | 38 + .../system/eddsa/ns3/Xexample.com.+016+09713.key | 1 + .../eddsa/ns3/Xexample.com.+016+09713.private | 3 + .../system/eddsa/ns3/Xexample.com.+016+38353.key | 1 + .../eddsa/ns3/Xexample.com.+016+38353.private | 3 + bin/tests/system/eddsa/ns3/example.com.db.in | 22 + bin/tests/system/eddsa/ns3/named.conf.in | 36 + bin/tests/system/eddsa/ns3/sign.sh | 37 + bin/tests/system/eddsa/prereq.sh | 26 + bin/tests/system/eddsa/setup.sh | 41 + bin/tests/system/eddsa/tests.sh | 87 + bin/tests/system/eddsa/tests_sh_eddsa.py | 14 + bin/tests/system/ednscompliance/clean.sh | 19 + bin/tests/system/ednscompliance/ns1/named.conf.in | 29 + bin/tests/system/ednscompliance/ns1/root.db | 21 + bin/tests/system/ednscompliance/setup.sh | 16 + bin/tests/system/ednscompliance/tests.sh | 114 + .../ednscompliance/tests_sh_ednscompliance.py | 14 + bin/tests/system/emptyzones/clean.sh | 19 + bin/tests/system/emptyzones/ns1/empty.db | 13 + bin/tests/system/emptyzones/ns1/named1.conf.in | 46 + bin/tests/system/emptyzones/ns1/named2.conf.in | 49 + bin/tests/system/emptyzones/ns1/rfc1918.zones | 32 + bin/tests/system/emptyzones/ns1/root.hint | 14 + bin/tests/system/emptyzones/setup.sh | 17 + bin/tests/system/emptyzones/tests.sh | 46 + bin/tests/system/emptyzones/tests_sh_emptyzones.py | 14 + .../enginepkcs11/2037-pk11_numbits-crash-test.pkt | 30 + bin/tests/system/enginepkcs11/clean.sh | 35 + bin/tests/system/enginepkcs11/ns1/named.args | 1 + bin/tests/system/enginepkcs11/ns1/named.conf.in | 36 + bin/tests/system/enginepkcs11/ns1/template.db.in | 24 + bin/tests/system/enginepkcs11/prereq.sh | 21 + bin/tests/system/enginepkcs11/setup.sh | 125 + bin/tests/system/enginepkcs11/tests.sh | 176 + .../system/enginepkcs11/tests_sh_enginepkcs11.py | 14 + bin/tests/system/feature-test.c | 224 + bin/tests/system/fetchlimit/ans4/ans.pl | 90 + bin/tests/system/fetchlimit/clean.sh | 21 + bin/tests/system/fetchlimit/ns1/named.conf.in | 35 + bin/tests/system/fetchlimit/ns1/root.db | 24 + bin/tests/system/fetchlimit/ns2/example.db | 37 + bin/tests/system/fetchlimit/ns2/named.conf.in | 41 + bin/tests/system/fetchlimit/ns3/named.args | 1 + bin/tests/system/fetchlimit/ns3/named1.conf.in | 47 + bin/tests/system/fetchlimit/ns3/named2.conf.in | 45 + bin/tests/system/fetchlimit/ns3/named3.conf.in | 45 + bin/tests/system/fetchlimit/ns3/root.hint | 14 + bin/tests/system/fetchlimit/ns5/named1.conf.in | 46 + bin/tests/system/fetchlimit/ns5/named2.conf.in | 49 + bin/tests/system/fetchlimit/ns5/root.hint | 14 + bin/tests/system/fetchlimit/setup.sh | 19 + bin/tests/system/fetchlimit/tests.sh | 325 + bin/tests/system/fetchlimit/tests_sh_fetchlimit.py | 14 + bin/tests/system/filter-aaaa/clean.sh | 33 + bin/tests/system/filter-aaaa/conf/bad1.conf | 17 + bin/tests/system/filter-aaaa/conf/bad2.conf | 26 + bin/tests/system/filter-aaaa/conf/bad3.conf | 19 + bin/tests/system/filter-aaaa/conf/bad4.conf | 19 + bin/tests/system/filter-aaaa/conf/bad5.conf | 21 + bin/tests/system/filter-aaaa/conf/good1.conf | 16 + bin/tests/system/filter-aaaa/conf/good2.conf | 16 + bin/tests/system/filter-aaaa/conf/good3.conf | 17 + bin/tests/system/filter-aaaa/conf/good4.conf | 17 + bin/tests/system/filter-aaaa/conf/good5.conf | 19 + bin/tests/system/filter-aaaa/ns1/named1.conf.in | 47 + bin/tests/system/filter-aaaa/ns1/named2.conf.in | 44 + bin/tests/system/filter-aaaa/ns1/root.db | 25 + bin/tests/system/filter-aaaa/ns1/sign.sh | 34 + bin/tests/system/filter-aaaa/ns1/signed.db.in | 25 + bin/tests/system/filter-aaaa/ns1/unsigned.db | 25 + bin/tests/system/filter-aaaa/ns2/hints | 16 + bin/tests/system/filter-aaaa/ns2/named1.conf.in | 44 + bin/tests/system/filter-aaaa/ns2/named2.conf.in | 44 + bin/tests/system/filter-aaaa/ns3/hints | 16 + bin/tests/system/filter-aaaa/ns3/named1.conf.in | 44 + bin/tests/system/filter-aaaa/ns3/named2.conf.in | 44 + bin/tests/system/filter-aaaa/ns4/named1.conf.in | 44 + bin/tests/system/filter-aaaa/ns4/named2.conf.in | 44 + bin/tests/system/filter-aaaa/ns4/root.db | 24 + bin/tests/system/filter-aaaa/ns4/sign.sh | 27 + bin/tests/system/filter-aaaa/ns4/signed.db.in | 25 + bin/tests/system/filter-aaaa/ns4/unsigned.db | 25 + bin/tests/system/filter-aaaa/ns5/hints | 16 + bin/tests/system/filter-aaaa/ns5/named.conf.in | 49 + bin/tests/system/filter-aaaa/setup.sh | 23 + bin/tests/system/filter-aaaa/tests.sh | 1420 ++ .../system/filter-aaaa/tests_sh_filter_aaaa.py | 14 + bin/tests/system/formerr/clean.sh | 21 + bin/tests/system/formerr/formerr.pl | 97 + bin/tests/system/formerr/nametoolong | 19 + bin/tests/system/formerr/noquestions | 1 + bin/tests/system/formerr/ns1/named.conf.in | 29 + bin/tests/system/formerr/ns1/root.db | 21 + bin/tests/system/formerr/setup.sh | 16 + bin/tests/system/formerr/tests.sh | 48 + bin/tests/system/formerr/tests_sh_formerr.py | 14 + bin/tests/system/formerr/twoquestions | 7 + bin/tests/system/forward/ans11/ans.py | 272 + bin/tests/system/forward/ans6/ans.pl | 562 + bin/tests/system/forward/clean.sh | 26 + bin/tests/system/forward/ns1/diditwork.net.db | 22 + bin/tests/system/forward/ns1/example.db | 23 + bin/tests/system/forward/ns1/named.conf.in | 87 + bin/tests/system/forward/ns1/net.example.lll | 15 + bin/tests/system/forward/ns1/root.db.in | 36 + bin/tests/system/forward/ns1/sign.sh | 34 + bin/tests/system/forward/ns1/sld.tld.db | 22 + bin/tests/system/forward/ns1/spoofed.net.db | 22 + bin/tests/system/forward/ns1/sub.local.net.db | 22 + bin/tests/system/forward/ns10/fakenet.zone | 17 + bin/tests/system/forward/ns10/fakenet2.zone | 15 + bin/tests/system/forward/ns10/fakesublocalnet.zone | 15 + bin/tests/system/forward/ns10/fakesublocaltld.zone | 15 + bin/tests/system/forward/ns10/named.conf.in | 54 + bin/tests/system/forward/ns10/net.example.lll | 15 + bin/tests/system/forward/ns10/spoofednet.zone | 16 + bin/tests/system/forward/ns2/example.db | 23 + bin/tests/system/forward/ns2/named.conf.in | 72 + bin/tests/system/forward/ns2/root.db | 30 + bin/tests/system/forward/ns2/tld.db | 29 + bin/tests/system/forward/ns3/named1.conf.in | 66 + bin/tests/system/forward/ns3/named2.conf.in | 43 + bin/tests/system/forward/ns3/root.db | 30 + bin/tests/system/forward/ns3/root2.db | 21 + bin/tests/system/forward/ns4/malicious.db | 24 + bin/tests/system/forward/ns4/named.conf.in | 69 + bin/tests/system/forward/ns4/root.db | 30 + bin/tests/system/forward/ns4/sibling.tld.db | 22 + bin/tests/system/forward/ns5/named.conf.in | 36 + bin/tests/system/forward/ns5/rebind.db | 24 + bin/tests/system/forward/ns5/root.db | 30 + bin/tests/system/forward/ns7/named.conf.in | 30 + bin/tests/system/forward/ns7/root.db | 30 + bin/tests/system/forward/ns8/named.conf.in | 35 + bin/tests/system/forward/ns8/root.db | 13 + bin/tests/system/forward/ns8/sub.local.tld.db | 15 + bin/tests/system/forward/ns9/local.net.db | 16 + bin/tests/system/forward/ns9/local.tld.db | 15 + bin/tests/system/forward/ns9/named1.conf.in | 67 + bin/tests/system/forward/ns9/named2.conf.in | 70 + bin/tests/system/forward/ns9/named3.conf.in | 50 + bin/tests/system/forward/ns9/named4.conf.in | 47 + bin/tests/system/forward/ns9/root.db | 13 + bin/tests/system/forward/rfc1918-inherited.conf | 17 + bin/tests/system/forward/rfc1918-notinherited.conf | 18 + bin/tests/system/forward/setup.sh | 30 + bin/tests/system/forward/tests.sh | 406 + bin/tests/system/forward/tests_sh_forward.py | 14 + bin/tests/system/forward/ula-inherited.conf | 17 + bin/tests/system/forward/ula-notinherited.conf | 18 + bin/tests/system/fromhex.pl | 47 + bin/tests/system/genzone.sh | 511 + bin/tests/system/geoip2/clean.sh | 20 + bin/tests/system/geoip2/conf/bad-areacode.conf | 38 + bin/tests/system/geoip2/conf/bad-dbname.conf | 30 + bin/tests/system/geoip2/conf/bad-netspeed.conf | 37 + bin/tests/system/geoip2/conf/bad-regiondb.conf | 44 + bin/tests/system/geoip2/conf/bad-threeletter.conf | 35 + bin/tests/system/geoip2/conf/good-options.conf | 36 + bin/tests/system/geoip2/data/GeoIP2-City.json | 506 + bin/tests/system/geoip2/data/GeoIP2-City.mmdb | Bin 0 -> 3165 bytes bin/tests/system/geoip2/data/GeoIP2-Country.json | 242 + bin/tests/system/geoip2/data/GeoIP2-Country.mmdb | Bin 0 -> 2971 bytes bin/tests/system/geoip2/data/GeoIP2-Domain.json | 72 + bin/tests/system/geoip2/data/GeoIP2-Domain.mmdb | Bin 0 -> 2524 bytes bin/tests/system/geoip2/data/GeoIP2-ISP.json | 86 + bin/tests/system/geoip2/data/GeoIP2-ISP.mmdb | Bin 0 -> 2626 bytes bin/tests/system/geoip2/data/GeoLite2-ASN.json | 86 + bin/tests/system/geoip2/data/GeoLite2-ASN.mmdb | Bin 0 -> 2672 bytes bin/tests/system/geoip2/data/README.md | 23 + bin/tests/system/geoip2/data/write-test-data.pl | 194 + bin/tests/system/geoip2/ns2/example.db.in | 21 + bin/tests/system/geoip2/ns2/named1.conf.in | 108 + bin/tests/system/geoip2/ns2/named10.conf.in | 100 + bin/tests/system/geoip2/ns2/named11.conf.in | 100 + bin/tests/system/geoip2/ns2/named12.conf.in | 41 + bin/tests/system/geoip2/ns2/named2.conf.in | 108 + bin/tests/system/geoip2/ns2/named3.conf.in | 100 + bin/tests/system/geoip2/ns2/named4.conf.in | 84 + bin/tests/system/geoip2/ns2/named5.conf.in | 92 + bin/tests/system/geoip2/ns2/named6.conf.in | 100 + bin/tests/system/geoip2/ns2/named7.conf.in | 100 + bin/tests/system/geoip2/ns2/named8.conf.in | 100 + bin/tests/system/geoip2/ns2/named9.conf.in | 100 + bin/tests/system/geoip2/prereq.sh | 20 + bin/tests/system/geoip2/setup.sh | 23 + bin/tests/system/geoip2/tests.sh | 490 + bin/tests/system/geoip2/tests_sh_geoip2.py | 14 + bin/tests/system/get_algorithms.py | 241 + bin/tests/system/get_core_dumps.sh | 70 + bin/tests/system/get_ports.sh | 72 + bin/tests/system/glue/clean.sh | 23 + bin/tests/system/glue/fi.good | 27 + bin/tests/system/glue/noglue.good | 14 + bin/tests/system/glue/ns1/named.conf.in | 39 + bin/tests/system/glue/ns1/net.db | 34 + bin/tests/system/glue/ns1/root-servers.nil.db | 25 + bin/tests/system/glue/ns1/root.db | 44 + bin/tests/system/glue/setup.sh | 16 + bin/tests/system/glue/tests.sh | 35 + bin/tests/system/glue/tests_sh_glue.py | 14 + bin/tests/system/hooks/clean.sh | 16 + bin/tests/system/hooks/driver/Makefile.am | 12 + bin/tests/system/hooks/driver/Makefile.in | 775 + bin/tests/system/hooks/driver/test-async.c | 348 + bin/tests/system/hooks/ns1/example.db | 21 + bin/tests/system/hooks/ns1/named.conf.in | 41 + bin/tests/system/hooks/setup.sh | 16 + bin/tests/system/hooks/tests_async_plugin.py | 27 + bin/tests/system/host/clean.sh | 21 + bin/tests/system/host/ns1/example.net.db | 31 + bin/tests/system/host/ns1/named.conf.in | 34 + bin/tests/system/host/setup.sh | 22 + bin/tests/system/host/tests.sh | 128 + bin/tests/system/host/tests_sh_host.py | 14 + bin/tests/system/idna/clean.sh | 19 + bin/tests/system/idna/ns1/named.conf.in | 30 + bin/tests/system/idna/ns1/root.db | 26 + bin/tests/system/idna/setup.sh | 17 + bin/tests/system/idna/tests.sh | 393 + bin/tests/system/idna/tests_sh_idna.py | 14 + bin/tests/system/ifconfig.sh.in | 268 + bin/tests/system/include-multiplecfg/clean.sh | 23 + .../system/include-multiplecfg/ns2/mars.com.db | 24 + bin/tests/system/include-multiplecfg/ns2/mars.conf | 18 + .../system/include-multiplecfg/ns2/named.conf.in | 29 + .../system/include-multiplecfg/ns2/zone1.com.db | 24 + .../system/include-multiplecfg/ns2/zone1.conf | 18 + .../system/include-multiplecfg/ns2/zone2.com.db | 24 + .../system/include-multiplecfg/ns2/zone2.conf | 18 + bin/tests/system/include-multiplecfg/setup.sh | 18 + bin/tests/system/include-multiplecfg/tests.sh | 64 + .../tests_sh_include_multiplecfg.py | 14 + bin/tests/system/inline/clean.sh | 27 + bin/tests/system/inline/ns1/named.conf.in | 36 + bin/tests/system/inline/ns1/root.db.in | 59 + bin/tests/system/inline/ns1/sign.sh | 25 + bin/tests/system/inline/ns2/bits.db.in | 22 + bin/tests/system/inline/ns2/named.conf.in | 86 + bin/tests/system/inline/ns2/nsec3-loop.db.in | 25 + bin/tests/system/inline/ns3/include.db.in | 12 + bin/tests/system/inline/ns3/named.conf.in | 181 + bin/tests/system/inline/ns3/primary.db.in | 21 + bin/tests/system/inline/ns3/primary2.db.in | 23 + bin/tests/system/inline/ns3/primary3.db.in | 24 + bin/tests/system/inline/ns3/primary4.db.in | 24 + bin/tests/system/inline/ns3/primary5.db.in | 24 + bin/tests/system/inline/ns3/primary6.db.in | 26 + bin/tests/system/inline/ns3/primary7.db.in | 26 + bin/tests/system/inline/ns3/sign.sh | 159 + bin/tests/system/inline/ns4/named.conf.in | 34 + bin/tests/system/inline/ns4/noixfr.db.in | 22 + bin/tests/system/inline/ns5/named.conf.post | 42 + bin/tests/system/inline/ns5/named.conf.pre | 39 + bin/tests/system/inline/ns6/named.conf.in | 41 + bin/tests/system/inline/ns7/named.conf.in | 50 + bin/tests/system/inline/ns7/sign.sh | 24 + bin/tests/system/inline/ns8/example.com.db.in | 21 + bin/tests/system/inline/ns8/example.db.in | 26 + bin/tests/system/inline/ns8/example2.db.in | 26 + bin/tests/system/inline/ns8/example3.db.in | 26 + bin/tests/system/inline/ns8/named.conf.in | 163 + bin/tests/system/inline/ns8/sign.sh | 35 + bin/tests/system/inline/setup.sh | 56 + bin/tests/system/inline/tests.sh | 1501 ++ bin/tests/system/inline/tests_sh_inline.py | 14 + bin/tests/system/inline/tests_signed_zone_files.py | 67 + bin/tests/system/integrity/clean.sh | 18 + bin/tests/system/integrity/ns1/mx-cname.db | 17 + bin/tests/system/integrity/ns1/named.conf.in | 115 + bin/tests/system/integrity/ns1/srv-cname.db | 17 + bin/tests/system/integrity/setup.sh | 16 + bin/tests/system/integrity/tests.sh | 132 + bin/tests/system/integrity/tests_sh_integrity.py | 14 + bin/tests/system/ixfr/ans2/startme | 0 bin/tests/system/ixfr/clean.sh | 26 + bin/tests/system/ixfr/ixfr-stats.good | 3 + bin/tests/system/ixfr/ns1/named.conf.in | 34 + bin/tests/system/ixfr/ns3/named.conf.in | 53 + bin/tests/system/ixfr/ns4/named.conf.in | 51 + bin/tests/system/ixfr/ns5/named.conf.in | 51 + bin/tests/system/ixfr/setup.sh | 68 + bin/tests/system/ixfr/tests.sh | 443 + bin/tests/system/ixfr/tests_sh_ixfr.py | 14 + bin/tests/system/journal/clean.sh | 22 + .../system/journal/ns1/changed.ver1.jnl.saved | Bin 0 -> 707 bytes .../system/journal/ns1/changed.ver2.jnl.saved | Bin 0 -> 718 bytes bin/tests/system/journal/ns1/d1212.jnl.saved | Bin 0 -> 1478 bytes bin/tests/system/journal/ns1/d2121.jnl.saved | Bin 0 -> 1478 bytes bin/tests/system/journal/ns1/generic.db.in | 17 + bin/tests/system/journal/ns1/ixfr.db.in | 18 + bin/tests/system/journal/ns1/ixfr.ver1.jnl.saved | Bin 0 -> 686 bytes bin/tests/system/journal/ns1/managed-keys.bind.in | 2 + .../system/journal/ns1/managed-keys.bind.jnl.in | 704 + bin/tests/system/journal/ns1/maxjournal.jnl.saved | Bin 0 -> 13660 bytes bin/tests/system/journal/ns1/maxjournal2.jnl.saved | Bin 0 -> 14259 bytes bin/tests/system/journal/ns1/named.conf.in | 92 + .../system/journal/ns1/unchanged.ver1.jnl.saved | Bin 0 -> 512 bytes .../system/journal/ns1/unchanged.ver2.jnl.saved | Bin 0 -> 512 bytes bin/tests/system/journal/ns2/managed-keys.bind.in | 14 + .../system/journal/ns2/managed-keys.bind.jnl.in | Bin 0 -> 2944 bytes bin/tests/system/journal/ns2/named.conf.in | 36 + bin/tests/system/journal/setup.sh | 51 + bin/tests/system/journal/tests.sh | 257 + bin/tests/system/journal/tests_sh_journal.py | 14 + bin/tests/system/kasp.sh | 1244 ++ bin/tests/system/kasp/README | 23 + bin/tests/system/kasp/clean.sh | 36 + bin/tests/system/kasp/kasp.conf | 27 + bin/tests/system/kasp/ns2/named.conf.in | 62 + bin/tests/system/kasp/ns2/secondary.kasp.db.in | 29 + bin/tests/system/kasp/ns2/secondary.kasp.db.in2 | 30 + bin/tests/system/kasp/ns2/setup.sh | 35 + bin/tests/system/kasp/ns2/template.tld.db.in | 27 + bin/tests/system/kasp/ns3/ed25519.conf | 29 + bin/tests/system/kasp/ns3/ed448.conf | 29 + bin/tests/system/kasp/ns3/named-fips.conf.in | 520 + bin/tests/system/kasp/ns3/named.conf.in | 30 + .../system/kasp/ns3/policies/autosign.conf.in | 133 + .../system/kasp/ns3/policies/kasp-fips.conf.in | 118 + bin/tests/system/kasp/ns3/policies/kasp.conf.in | 34 + bin/tests/system/kasp/ns3/setup.sh | 1470 ++ bin/tests/system/kasp/ns3/template.db.in | 27 + bin/tests/system/kasp/ns3/template2.db.in | 27 + bin/tests/system/kasp/ns4/example1.db.in | 24 + bin/tests/system/kasp/ns4/example2.db.in | 24 + bin/tests/system/kasp/ns4/named.conf.in | 177 + bin/tests/system/kasp/ns4/setup.sh | 33 + bin/tests/system/kasp/ns4/template.db.in | 27 + bin/tests/system/kasp/ns5/named.conf.in | 133 + bin/tests/system/kasp/ns5/setup.sh | 30 + bin/tests/system/kasp/ns5/template.db.in | 27 + bin/tests/system/kasp/ns6/example.db.in | 26 + bin/tests/system/kasp/ns6/example2.db.in | 26 + bin/tests/system/kasp/ns6/example3.db.in | 26 + bin/tests/system/kasp/ns6/named.conf.in | 98 + bin/tests/system/kasp/ns6/named2.conf.in | 186 + bin/tests/system/kasp/ns6/policies/csk1.conf.in | 30 + bin/tests/system/kasp/ns6/policies/csk2.conf.in | 30 + .../system/kasp/ns6/policies/kasp-fips.conf.in | 63 + bin/tests/system/kasp/ns6/policies/kasp.conf.in | 33 + bin/tests/system/kasp/ns6/setup.sh | 409 + bin/tests/system/kasp/ns6/template.db.in | 27 + bin/tests/system/kasp/setup.sh | 80 + bin/tests/system/kasp/tests.sh | 4883 +++++ bin/tests/system/kasp/tests_sh_kasp.py | 14 + bin/tests/system/keepalive/clean.sh | 21 + bin/tests/system/keepalive/expected | 4 + bin/tests/system/keepalive/ns1/named.conf.in | 39 + bin/tests/system/keepalive/ns1/root.db | 24 + bin/tests/system/keepalive/ns2/example.db | 16 + bin/tests/system/keepalive/ns2/named.conf.in | 45 + bin/tests/system/keepalive/ns3/named.conf.in | 45 + bin/tests/system/keepalive/setup.sh | 18 + bin/tests/system/keepalive/tests.sh | 99 + bin/tests/system/keepalive/tests_sh_keepalive.py | 14 + bin/tests/system/keyfromlabel/clean.sh | 27 + bin/tests/system/keyfromlabel/prereq.sh | 21 + bin/tests/system/keyfromlabel/setup.sh | 22 + bin/tests/system/keyfromlabel/template.db.in | 24 + bin/tests/system/keyfromlabel/tests.sh | 93 + .../system/keyfromlabel/tests_sh_keyfromlabel.py | 14 + bin/tests/system/keymgr2kasp/README | 17 + bin/tests/system/keymgr2kasp/clean.sh | 34 + bin/tests/system/keymgr2kasp/ns3/kasp.conf.in | 104 + bin/tests/system/keymgr2kasp/ns3/named.conf.in | 106 + bin/tests/system/keymgr2kasp/ns3/named2.conf.in | 95 + bin/tests/system/keymgr2kasp/ns3/setup.sh | 148 + bin/tests/system/keymgr2kasp/ns3/template.db.in | 27 + bin/tests/system/keymgr2kasp/ns4/named.conf.in | 73 + bin/tests/system/keymgr2kasp/ns4/named2.conf.in | 90 + bin/tests/system/keymgr2kasp/ns4/setup.sh | 46 + .../system/keymgr2kasp/ns4/template.ext.db.in | 24 + .../system/keymgr2kasp/ns4/template.int.db.in | 24 + bin/tests/system/keymgr2kasp/setup.sh | 34 + bin/tests/system/keymgr2kasp/tests.sh | 1276 ++ .../system/keymgr2kasp/tests_sh_keymgr2kasp.py | 14 + bin/tests/system/legacy.run.sh.in | 311 + bin/tests/system/legacy/build.sh | 21 + bin/tests/system/legacy/clean.sh | 31 + bin/tests/system/legacy/ns1/named1.conf.in | 38 + bin/tests/system/legacy/ns1/named2.conf.in | 31 + bin/tests/system/legacy/ns1/root.db | 33 + bin/tests/system/legacy/ns1/trusted.conf | 16 + bin/tests/system/legacy/ns10/ednsrefused.db | 14 + bin/tests/system/legacy/ns10/named.conf.in | 29 + bin/tests/system/legacy/ns10/named.ednsrefused | 1 + bin/tests/system/legacy/ns2/dropedns.db | 14 + bin/tests/system/legacy/ns2/named.conf.in | 29 + bin/tests/system/legacy/ns2/named.dropedns | 1 + bin/tests/system/legacy/ns3/dropedns-notcp.db | 14 + bin/tests/system/legacy/ns3/named.conf.in | 29 + bin/tests/system/legacy/ns3/named.dropedns | 1 + bin/tests/system/legacy/ns3/named.notcp | 1 + bin/tests/system/legacy/ns4/named.args | 1 + bin/tests/system/legacy/ns4/named.conf.in | 29 + bin/tests/system/legacy/ns4/plain.db | 14 + bin/tests/system/legacy/ns5/named.args | 1 + bin/tests/system/legacy/ns5/named.conf.in | 29 + bin/tests/system/legacy/ns5/named.notcp | 1 + bin/tests/system/legacy/ns5/plain-notcp.db | 14 + bin/tests/system/legacy/ns6/edns512.db.in | 15 + bin/tests/system/legacy/ns6/edns512.db.signed | 226 + bin/tests/system/legacy/ns6/named.args | 1 + bin/tests/system/legacy/ns6/named.conf.in | 29 + bin/tests/system/legacy/ns6/sign.sh | 30 + bin/tests/system/legacy/ns7/edns512-notcp.db.in | 15 + .../system/legacy/ns7/edns512-notcp.db.signed | 226 + bin/tests/system/legacy/ns7/named.args | 1 + bin/tests/system/legacy/ns7/named.conf.in | 29 + bin/tests/system/legacy/ns7/named.notcp | 1 + bin/tests/system/legacy/ns7/sign.sh | 33 + bin/tests/system/legacy/ns8/ednsformerr.db | 14 + bin/tests/system/legacy/ns8/named.conf.in | 29 + bin/tests/system/legacy/ns8/named.ednsformerr | 1 + bin/tests/system/legacy/ns9/ednsnotimp.db | 14 + bin/tests/system/legacy/ns9/named.conf.in | 29 + bin/tests/system/legacy/ns9/named.ednsnotimp | 1 + bin/tests/system/legacy/setup.sh | 25 + bin/tests/system/legacy/tests.sh | 269 + bin/tests/system/legacy/tests_sh_legacy.py | 14 + bin/tests/system/limits/clean.sh | 22 + bin/tests/system/limits/knowngood.dig.out.1000 | 1023 + bin/tests/system/limits/knowngood.dig.out.2000 | 2023 ++ bin/tests/system/limits/knowngood.dig.out.3000 | 3023 +++ bin/tests/system/limits/knowngood.dig.out.4000 | 4023 ++++ .../limits/knowngood.dig.out.a-maximum-rrset | 4114 ++++ bin/tests/system/limits/ns1/example.db | 19112 +++++++++++++++++++ bin/tests/system/limits/ns1/named.conf.in | 36 + bin/tests/system/limits/ns1/root.db | 24 + bin/tests/system/limits/setup.sh | 16 + bin/tests/system/limits/tests.sh | 57 + bin/tests/system/limits/tests_sh_limits.py | 14 + bin/tests/system/logfileconfig/clean.sh | 38 + bin/tests/system/logfileconfig/named1.args | 1 + bin/tests/system/logfileconfig/named2.args | 1 + .../system/logfileconfig/ns1/named.abspathconf.in | 52 + .../system/logfileconfig/ns1/named.dirconf.in | 43 + .../system/logfileconfig/ns1/named.incconf.in | 52 + .../system/logfileconfig/ns1/named.iso8601-utc.in | 43 + .../system/logfileconfig/ns1/named.iso8601.in | 44 + .../system/logfileconfig/ns1/named.pipeconf.in | 43 + bin/tests/system/logfileconfig/ns1/named.plain.in | 50 + .../system/logfileconfig/ns1/named.plainconf.in | 34 + .../system/logfileconfig/ns1/named.symconf.in | 43 + bin/tests/system/logfileconfig/ns1/named.tsconf.in | 52 + .../system/logfileconfig/ns1/named.unlimited.in | 52 + .../system/logfileconfig/ns1/named.versconf.in | 52 + bin/tests/system/logfileconfig/setup.sh | 18 + bin/tests/system/logfileconfig/tests.sh | 297 + .../system/logfileconfig/tests_sh_logfileconfig.py | 14 + bin/tests/system/makejournal.c | 158 + bin/tests/system/masterfile/clean.sh | 20 + bin/tests/system/masterfile/knowngood.dig.out | 32 + bin/tests/system/masterfile/ns1/include.db | 35 + bin/tests/system/masterfile/ns1/named.conf.in | 40 + bin/tests/system/masterfile/ns1/sub.db | 15 + bin/tests/system/masterfile/ns1/ttl1.db | 27 + bin/tests/system/masterfile/ns1/ttl2.db | 30 + bin/tests/system/masterfile/ns2/example.db | 21 + bin/tests/system/masterfile/ns2/named.conf.in | 42 + bin/tests/system/masterfile/setup.sh | 18 + bin/tests/system/masterfile/tests.sh | 63 + bin/tests/system/masterfile/tests_sh_masterfile.py | 14 + .../masterfile/zone/inheritownerafterinclude.db | 14 + .../masterfile/zone/inheritownerafterinclude.good | 3 + bin/tests/system/masterfile/zone/nameservers.db | 12 + bin/tests/system/masterformat/clean.sh | 35 + bin/tests/system/masterformat/ns1/compile.sh | 34 + bin/tests/system/masterformat/ns1/example.db | 58 + bin/tests/system/masterformat/ns1/large.db.in | 22 + bin/tests/system/masterformat/ns1/named.conf.in | 88 + bin/tests/system/masterformat/ns1/signed.db | 29 + .../system/masterformat/ns2/formerly-text.db.in | 48 + bin/tests/system/masterformat/ns2/named.conf.in | 64 + bin/tests/system/masterformat/ns3/named.conf.in | 46 + bin/tests/system/masterformat/setup.sh | 31 + bin/tests/system/masterformat/tests.sh | 278 + .../system/masterformat/tests_sh_masterformat.py | 14 + bin/tests/system/metadata/child.db | 24 + bin/tests/system/metadata/clean.sh | 21 + bin/tests/system/metadata/parent.db | 31 + bin/tests/system/metadata/setup.sh | 60 + bin/tests/system/metadata/tests.sh | 233 + bin/tests/system/metadata/tests_sh_metadata.py | 14 + bin/tests/system/mirror/README | 26 + bin/tests/system/mirror/clean.sh | 30 + bin/tests/system/mirror/ns1/named.conf.in | 29 + bin/tests/system/mirror/ns1/root.db.in | 19 + bin/tests/system/mirror/ns1/sign.sh | 37 + bin/tests/system/mirror/ns2/example.db.in | 17 + .../system/mirror/ns2/initially-unavailable.db.in | 16 + bin/tests/system/mirror/ns2/named.conf.in | 86 + bin/tests/system/mirror/ns2/sign.sh | 79 + bin/tests/system/mirror/ns2/sub.example.db.in | 15 + bin/tests/system/mirror/ns2/verify.db.in | 15 + bin/tests/system/mirror/ns3/named.args | 1 + bin/tests/system/mirror/ns3/named.conf.in | 102 + bin/tests/system/mirror/setup.sh | 25 + bin/tests/system/mirror/tests.sh | 558 + bin/tests/system/mirror/tests_sh_mirror.py | 14 + bin/tests/system/mkeys/README | 34 + bin/tests/system/mkeys/clean.sh | 33 + bin/tests/system/mkeys/ns1/named1.conf.in | 59 + bin/tests/system/mkeys/ns1/named2.conf.in | 57 + bin/tests/system/mkeys/ns1/named3.conf.in | 51 + bin/tests/system/mkeys/ns1/root.db | 28 + bin/tests/system/mkeys/ns1/sign.sh | 93 + bin/tests/system/mkeys/ns1/sub.tld.db | 21 + bin/tests/system/mkeys/ns1/tld.db | 23 + bin/tests/system/mkeys/ns1/unsupported.key | 1 + bin/tests/system/mkeys/ns2/named.args | 1 + bin/tests/system/mkeys/ns2/named.conf.in | 43 + bin/tests/system/mkeys/ns3/named.args | 1 + bin/tests/system/mkeys/ns3/named.conf.in | 45 + bin/tests/system/mkeys/ns4/named.conf.in | 48 + bin/tests/system/mkeys/ns4/sign.sh | 24 + bin/tests/system/mkeys/ns4/sub.foo.db | 21 + bin/tests/system/mkeys/ns5/foo.db | 23 + bin/tests/system/mkeys/ns5/named.conf.in | 51 + bin/tests/system/mkeys/ns5/named1.args | 1 + bin/tests/system/mkeys/ns5/named2.args | 1 + bin/tests/system/mkeys/ns6/named.args | 1 + bin/tests/system/mkeys/ns6/named.conf.in | 44 + bin/tests/system/mkeys/ns6/setup.sh | 33 + bin/tests/system/mkeys/ns6/unsupported-managed.key | 1 + bin/tests/system/mkeys/ns7/named.conf.in | 51 + bin/tests/system/mkeys/setup.sh | 43 + bin/tests/system/mkeys/tests.sh | 882 + bin/tests/system/mkeys/tests_sh_mkeys.py | 14 + bin/tests/system/names/clean.sh | 20 + bin/tests/system/names/ns1/example.db | 50 + bin/tests/system/names/ns1/named.conf.in | 45 + bin/tests/system/names/setup.sh | 16 + bin/tests/system/names/tests.sh | 48 + bin/tests/system/names/tests_sh_names.py | 14 + bin/tests/system/notify/clean.sh | 39 + bin/tests/system/notify/ns1/named.conf.in | 30 + bin/tests/system/notify/ns1/root.db | 24 + bin/tests/system/notify/ns2/example1.db | 144 + bin/tests/system/notify/ns2/example2.db | 144 + bin/tests/system/notify/ns2/example3.db | 144 + bin/tests/system/notify/ns2/example4.db | 144 + bin/tests/system/notify/ns2/generic.db | 25 + bin/tests/system/notify/ns2/named.conf.in | 86 + bin/tests/system/notify/ns3/named.conf.in | 44 + .../system/notify/ns3/notify-source-port-test.db | 22 + bin/tests/system/notify/ns4/named.conf.in | 36 + bin/tests/system/notify/ns4/named.port.in | 1 + bin/tests/system/notify/ns5/named.conf.in | 70 + bin/tests/system/notify/ns5/x21.db | 22 + bin/tests/system/notify/setup.sh | 25 + bin/tests/system/notify/tests.sh | 220 + bin/tests/system/notify/tests_sh_notify.py | 14 + bin/tests/system/nsec3/clean.sh | 22 + bin/tests/system/nsec3/ns2/named.conf.in | 47 + bin/tests/system/nsec3/ns2/setup.sh | 22 + bin/tests/system/nsec3/ns2/template.db.in | 28 + bin/tests/system/nsec3/ns3/named.conf.in | 218 + bin/tests/system/nsec3/ns3/named2.conf.in | 212 + .../nsec3/ns3/nsec3-fails-to-load.kasp.db.in | 19 + bin/tests/system/nsec3/ns3/setup.sh | 60 + bin/tests/system/nsec3/ns3/template.db.in | 27 + bin/tests/system/nsec3/setup.sh | 30 + bin/tests/system/nsec3/tests.sh | 553 + bin/tests/system/nsec3/tests_sh_nsec3.py | 14 + bin/tests/system/nslookup/clean.sh | 21 + bin/tests/system/nslookup/ns1/example.net.db | 31 + bin/tests/system/nslookup/ns1/named.conf.in | 34 + bin/tests/system/nslookup/setup.sh | 20 + bin/tests/system/nslookup/tests.sh | 159 + bin/tests/system/nslookup/tests_sh_nslookup.py | 14 + bin/tests/system/nsupdate/ans4/ans.pl | 65 + bin/tests/system/nsupdate/clean.sh | 71 + bin/tests/system/nsupdate/commandlist | 15 + bin/tests/system/nsupdate/knowngood.ns1.after | 99 + bin/tests/system/nsupdate/knowngood.ns1.afterstop | 3 + bin/tests/system/nsupdate/knowngood.ns1.before | 98 + bin/tests/system/nsupdate/krb/setup.sh | 117 + bin/tests/system/nsupdate/ns1/example1.db | 146 + .../nsupdate/ns1/legacy/Klegacy-157.+157+23571.key | 1 + .../ns1/legacy/Klegacy-157.+157+23571.private | 7 + .../nsupdate/ns1/legacy/Klegacy-161.+161+23350.key | 1 + .../ns1/legacy/Klegacy-161.+161+23350.private | 7 + .../nsupdate/ns1/legacy/Klegacy-162.+162+00032.key | 1 + .../ns1/legacy/Klegacy-162.+162+00032.private | 7 + .../nsupdate/ns1/legacy/Klegacy-163.+163+48857.key | 1 + .../ns1/legacy/Klegacy-163.+163+48857.private | 7 + .../nsupdate/ns1/legacy/Klegacy-164.+164+09001.key | 1 + .../ns1/legacy/Klegacy-164.+164+09001.private | 7 + .../nsupdate/ns1/legacy/Klegacy-165.+165+61012.key | 1 + .../ns1/legacy/Klegacy-165.+165+61012.private | 7 + bin/tests/system/nsupdate/ns1/many.test.db.in | 21 + bin/tests/system/nsupdate/ns1/max-ttl.db | 29 + bin/tests/system/nsupdate/ns1/maxjournal.db.in | 20 + bin/tests/system/nsupdate/ns1/named.conf.in | 175 + bin/tests/system/nsupdate/ns1/sample.db.in | 19 + bin/tests/system/nsupdate/ns10/dns.keytab | Bin 0 -> 168 bytes bin/tests/system/nsupdate/ns10/example.com.db.in | 27 + bin/tests/system/nsupdate/ns10/in-addr.db.in | 27 + bin/tests/system/nsupdate/ns10/machine.ccache | Bin 0 -> 1217 bytes bin/tests/system/nsupdate/ns10/named.conf.in | 52 + bin/tests/system/nsupdate/ns2/named.conf.in | 75 + bin/tests/system/nsupdate/ns2/sample.db.in | 21 + .../system/nsupdate/ns3/delegation.test.db.in | 15 + bin/tests/system/nsupdate/ns3/dnskey.test.db.in | 15 + bin/tests/system/nsupdate/ns3/example.db.in | 15 + .../system/nsupdate/ns3/multisigner.test.db.in | 14 + bin/tests/system/nsupdate/ns3/named.conf.in | 73 + .../system/nsupdate/ns3/nsec3param.test.db.in | 15 + bin/tests/system/nsupdate/ns3/sign.sh | 50 + bin/tests/system/nsupdate/ns3/too-big.test.db.in | 15 + bin/tests/system/nsupdate/ns5/local.db.in | 25 + bin/tests/system/nsupdate/ns5/named.args | 1 + bin/tests/system/nsupdate/ns5/named.conf.in | 41 + bin/tests/system/nsupdate/ns6/in-addr.db.in | 21 + bin/tests/system/nsupdate/ns6/named.args | 1 + bin/tests/system/nsupdate/ns6/named.conf.in | 41 + bin/tests/system/nsupdate/ns7/dns.keytab | Bin 0 -> 166 bytes bin/tests/system/nsupdate/ns7/example.com.db.in | 27 + bin/tests/system/nsupdate/ns7/in-addr.db.in | 27 + bin/tests/system/nsupdate/ns7/machine.ccache | Bin 0 -> 1327 bytes bin/tests/system/nsupdate/ns7/named1.conf.in | 52 + bin/tests/system/nsupdate/ns7/named2.conf.in | 53 + .../nsupdate/ns8/dns-other-than-KRB5_KTNAME.keytab | Bin 0 -> 166 bytes bin/tests/system/nsupdate/ns8/example.com.db.in | 21 + bin/tests/system/nsupdate/ns8/in-addr.db.in | 21 + bin/tests/system/nsupdate/ns8/machine.ccache | Bin 0 -> 1327 bytes bin/tests/system/nsupdate/ns8/named.conf.in | 51 + bin/tests/system/nsupdate/ns9/dns.keytab | Bin 0 -> 166 bytes bin/tests/system/nsupdate/ns9/example.com.db.in | 21 + bin/tests/system/nsupdate/ns9/in-addr.db.in | 21 + bin/tests/system/nsupdate/ns9/machine.ccache | Bin 0 -> 1215 bytes bin/tests/system/nsupdate/ns9/named.conf.in | 65 + bin/tests/system/nsupdate/resolv.conf | 15 + bin/tests/system/nsupdate/setup.sh | 116 + bin/tests/system/nsupdate/tests.sh | 2262 +++ bin/tests/system/nsupdate/tests_sh_nsupdate.py | 14 + bin/tests/system/nsupdate/update_test.pl | 429 + bin/tests/system/nsupdate/verylarge.in | 3 + bin/tests/system/nzd2nzf/clean.sh | 21 + bin/tests/system/nzd2nzf/ns1/added.db | 26 + bin/tests/system/nzd2nzf/ns1/named.conf.in | 32 + bin/tests/system/nzd2nzf/prereq.sh | 20 + bin/tests/system/nzd2nzf/setup.sh | 16 + bin/tests/system/nzd2nzf/tests.sh | 85 + bin/tests/system/nzd2nzf/tests_sh_nzd2nzf.py | 14 + bin/tests/system/org.isc.bind.system | 29 + bin/tests/system/org.isc.bind.system.plist | 17 + bin/tests/system/packet.pl | 164 + bin/tests/system/padding/clean.sh | 21 + bin/tests/system/padding/ns1/named.conf.in | 39 + bin/tests/system/padding/ns1/root.db | 24 + bin/tests/system/padding/ns2/example.db | 15 + bin/tests/system/padding/ns2/named.conf.in | 45 + bin/tests/system/padding/ns3/named.conf.in | 45 + bin/tests/system/padding/ns4/named.conf.in | 45 + bin/tests/system/padding/setup.sh | 23 + bin/tests/system/padding/tests.sh | 135 + bin/tests/system/padding/tests_sh_padding.py | 14 + bin/tests/system/parallel.sh | 34 + bin/tests/system/pending/clean.sh | 27 + bin/tests/system/pending/ns1/named.conf.in | 31 + bin/tests/system/pending/ns1/root.db.in | 29 + bin/tests/system/pending/ns1/sign.sh | 35 + bin/tests/system/pending/ns2/example.com.db.in | 27 + bin/tests/system/pending/ns2/example.db.in | 26 + bin/tests/system/pending/ns2/forgery.db | 24 + bin/tests/system/pending/ns2/named.conf.in | 51 + bin/tests/system/pending/ns2/sign.sh | 33 + bin/tests/system/pending/ns3/hostile.db | 22 + bin/tests/system/pending/ns3/mail.example.db | 23 + bin/tests/system/pending/ns3/named.conf.in | 44 + bin/tests/system/pending/ns4/named.conf.in | 31 + bin/tests/system/pending/setup.sh | 23 + bin/tests/system/pending/tests.sh | 200 + bin/tests/system/pending/tests_sh_pending.py | 14 + bin/tests/system/pipelined/ans5/ans.py | 212 + bin/tests/system/pipelined/clean.sh | 19 + bin/tests/system/pipelined/input | 8 + bin/tests/system/pipelined/inputb | 8 + bin/tests/system/pipelined/ns1/named.conf.in | 39 + bin/tests/system/pipelined/ns1/root.db | 27 + bin/tests/system/pipelined/ns2/examplea.db | 32 + bin/tests/system/pipelined/ns2/named.conf.in | 45 + bin/tests/system/pipelined/ns3/exampleb.db | 32 + bin/tests/system/pipelined/ns3/named.conf.in | 45 + bin/tests/system/pipelined/ns4/named.conf.in | 41 + bin/tests/system/pipelined/pipequeries.c | 303 + bin/tests/system/pipelined/ref | 8 + bin/tests/system/pipelined/refb | 8 + bin/tests/system/pipelined/setup.sh | 21 + bin/tests/system/pipelined/tests.sh | 82 + bin/tests/system/pipelined/tests_sh_pipelined.py | 14 + bin/tests/system/pytest.ini | 20 + bin/tests/system/pytest_custom_markers.py | 60 + bin/tests/system/qmin/ans2/ans.py | 401 + bin/tests/system/qmin/ans3/ans.py | 274 + bin/tests/system/qmin/ans4/ans.py | 320 + bin/tests/system/qmin/clean.sh | 20 + bin/tests/system/qmin/ns1/named.conf.in | 32 + bin/tests/system/qmin/ns1/root.db | 41 + bin/tests/system/qmin/ns5/named.conf.in | 43 + bin/tests/system/qmin/ns6/named.conf.in | 43 + bin/tests/system/qmin/ns7/named.conf.in | 51 + bin/tests/system/qmin/setup.sh | 19 + bin/tests/system/qmin/tests.sh | 537 + bin/tests/system/qmin/tests_sh_qmin.py | 18 + bin/tests/system/reclimit/README | 19 + bin/tests/system/reclimit/ans2/ans.pl | 235 + bin/tests/system/reclimit/ans4/ans.pl | 240 + bin/tests/system/reclimit/ans7/ans.pl | 76 + bin/tests/system/reclimit/clean.sh | 22 + bin/tests/system/reclimit/ns1/named.conf.in | 27 + bin/tests/system/reclimit/ns1/root.db | 21 + bin/tests/system/reclimit/ns3/hints.db | 13 + bin/tests/system/reclimit/ns3/named1.conf.in | 39 + bin/tests/system/reclimit/ns3/named2.conf.in | 39 + bin/tests/system/reclimit/ns3/named3.conf.in | 40 + bin/tests/system/reclimit/ns3/named4.conf.in | 40 + bin/tests/system/reclimit/setup.sh | 17 + bin/tests/system/reclimit/tests.sh | 212 + bin/tests/system/reclimit/tests_sh_reclimit.py | 18 + bin/tests/system/redirect/clean.sh | 38 + bin/tests/system/redirect/conf/bad1.conf | 25 + bin/tests/system/redirect/conf/bad2.conf | 25 + bin/tests/system/redirect/conf/bad3.conf | 24 + bin/tests/system/redirect/conf/good1.conf | 22 + bin/tests/system/redirect/conf/good2.conf | 22 + bin/tests/system/redirect/conf/good3.conf | 23 + bin/tests/system/redirect/conf/good4.conf | 23 + bin/tests/system/redirect/ns1/example.db | 50 + bin/tests/system/redirect/ns1/named.conf.in | 57 + bin/tests/system/redirect/ns1/redirect.db | 20 + bin/tests/system/redirect/ns1/root.db | 19 + bin/tests/system/redirect/ns1/sign.sh | 36 + bin/tests/system/redirect/ns2/example.db.in | 16 + bin/tests/system/redirect/ns2/named.conf.in | 57 + bin/tests/system/redirect/ns2/redirect.db.in | 20 + bin/tests/system/redirect/ns3/example.db | 50 + bin/tests/system/redirect/ns3/named.conf.in | 54 + bin/tests/system/redirect/ns3/redirect.db | 16 + bin/tests/system/redirect/ns3/root.db | 20 + bin/tests/system/redirect/ns3/sign.sh | 36 + bin/tests/system/redirect/ns4/example.db.in | 16 + bin/tests/system/redirect/ns4/named.conf.in | 51 + bin/tests/system/redirect/ns4/root.hint | 14 + bin/tests/system/redirect/ns5/named.conf.in | 33 + bin/tests/system/redirect/ns5/root.db.in | 18 + bin/tests/system/redirect/ns5/sign.sh | 44 + bin/tests/system/redirect/ns5/signed.db.in | 20 + bin/tests/system/redirect/ns5/unsigned.db | 20 + bin/tests/system/redirect/ns6/named.conf.in | 33 + bin/tests/system/redirect/ns6/root.db | 18 + bin/tests/system/redirect/setup.sh | 29 + bin/tests/system/redirect/tests.sh | 549 + bin/tests/system/redirect/tests_sh_redirect.py | 14 + bin/tests/system/resolve.c | 505 + bin/tests/system/resolver/ans10/ans.py | 152 + bin/tests/system/resolver/ans2/ans.pl | 147 + bin/tests/system/resolver/ans3/ans.pl | 191 + bin/tests/system/resolver/ans8/ans.pl | 177 + bin/tests/system/resolver/clean.sh | 40 + bin/tests/system/resolver/ns1/chaostest.db | 16 + bin/tests/system/resolver/ns1/named.conf.in | 79 + bin/tests/system/resolver/ns1/root.hint | 14 + bin/tests/system/resolver/ns4/broken.db | 24 + bin/tests/system/resolver/ns4/child.server.db | 23 + bin/tests/system/resolver/ns4/moves.db | 22 + bin/tests/system/resolver/ns4/named.conf.in | 72 + bin/tests/system/resolver/ns4/named.noaa | 12 + bin/tests/system/resolver/ns4/root.db | 39 + bin/tests/system/resolver/ns4/sourcens.db | 91 + bin/tests/system/resolver/ns4/tld1.db | 35 + bin/tests/system/resolver/ns4/tld2.db | 35 + bin/tests/system/resolver/ns4/v4only.net.db | 22 + bin/tests/system/resolver/ns5/child.server.db | 23 + bin/tests/system/resolver/ns5/moves.db | 22 + bin/tests/system/resolver/ns5/named.conf.in | 60 + bin/tests/system/resolver/ns5/root.hint | 14 + bin/tests/system/resolver/ns6/broken.db | 28 + bin/tests/system/resolver/ns6/delegation-only.db | 33 + bin/tests/system/resolver/ns6/ds.example.net.db.in | 15 + bin/tests/system/resolver/ns6/example.net.db.in | 34 + bin/tests/system/resolver/ns6/fetch.tld.db | 23 + bin/tests/system/resolver/ns6/keygen.sh | 38 + bin/tests/system/resolver/ns6/moves.db | 22 + bin/tests/system/resolver/ns6/named.conf.in | 102 + .../system/resolver/ns6/no-edns-version.tld.db | 14 + bin/tests/system/resolver/ns6/redirect.com.db | 27 + bin/tests/system/resolver/ns6/root.db | 36 + bin/tests/system/resolver/ns6/targetns.db | 25 + bin/tests/system/resolver/ns6/tld1.db | 17 + .../system/resolver/ns6/to-be-removed.tld.db.in | 28 + bin/tests/system/resolver/ns7/all-cnames.db | 20 + bin/tests/system/resolver/ns7/edns-version.tld.db | 14 + bin/tests/system/resolver/ns7/named1.conf.in | 75 + bin/tests/system/resolver/ns7/named2.conf.in | 75 + bin/tests/system/resolver/ns7/root.hint | 14 + bin/tests/system/resolver/ns7/server.db.in | 24 + bin/tests/system/resolver/ns7/sub.tld1.db | 17 + bin/tests/system/resolver/ns7/tld2.db | 18 + bin/tests/system/resolver/ns9/named.args | 2 + bin/tests/system/resolver/ns9/named.conf.in | 39 + bin/tests/system/resolver/ns9/named.ipv6-only | 0 bin/tests/system/resolver/ns9/root.hint | 15 + bin/tests/system/resolver/setup.sh | 28 + bin/tests/system/resolver/tests.sh | 1022 + bin/tests/system/resolver/tests_sh_resolver.py | 14 + bin/tests/system/rndc/clean.sh | 33 + bin/tests/system/rndc/gencheck.c | 90 + bin/tests/system/rndc/ns2/incl.db | 13 + bin/tests/system/rndc/ns2/named.conf.in | 65 + bin/tests/system/rndc/ns2/secondkey.conf | 21 + bin/tests/system/rndc/ns3/named.conf.in | 49 + bin/tests/system/rndc/ns4/named.conf.in | 38 + bin/tests/system/rndc/ns5/named.conf.in | 35 + bin/tests/system/rndc/ns6/named.args | 3 + bin/tests/system/rndc/ns6/named.conf.in | 30 + bin/tests/system/rndc/ns7/include.db.in | 16 + bin/tests/system/rndc/ns7/include2.db.in | 16 + bin/tests/system/rndc/ns7/named.conf.in | 58 + bin/tests/system/rndc/ns7/test.db.in | 13 + bin/tests/system/rndc/setup.sh | 64 + bin/tests/system/rndc/tests.sh | 814 + bin/tests/system/rndc/tests_sh_rndc.py | 14 + bin/tests/system/rootkeysentinel/clean.sh | 26 + bin/tests/system/rootkeysentinel/ns1/named.conf.in | 32 + bin/tests/system/rootkeysentinel/ns1/root.db.in | 24 + bin/tests/system/rootkeysentinel/ns1/sign.sh | 35 + bin/tests/system/rootkeysentinel/ns2/example.db.in | 21 + bin/tests/system/rootkeysentinel/ns2/named.conf.in | 32 + bin/tests/system/rootkeysentinel/ns2/sign.sh | 40 + bin/tests/system/rootkeysentinel/ns3/hint.db | 13 + bin/tests/system/rootkeysentinel/ns3/named.conf.in | 33 + bin/tests/system/rootkeysentinel/ns4/hint.db | 13 + bin/tests/system/rootkeysentinel/ns4/named.conf.in | 33 + bin/tests/system/rootkeysentinel/setup.sh | 22 + bin/tests/system/rootkeysentinel/tests.sh | 295 + .../rootkeysentinel/tests_sh_rootkeysentinel.py | 14 + bin/tests/system/rpz/README | 36 + bin/tests/system/rpz/clean.sh | 57 + bin/tests/system/rpz/dnsrps.c | 172 + bin/tests/system/rpz/dnsrpzd-license.conf | 23 + bin/tests/system/rpz/dnsrpzd.conf.in | 62 + bin/tests/system/rpz/ns1/named.conf.in | 37 + bin/tests/system/rpz/ns1/root.db | 42 + bin/tests/system/rpz/ns10/hints | 13 + bin/tests/system/rpz/ns10/named.conf.in | 42 + bin/tests/system/rpz/ns10/stub.db | 21 + bin/tests/system/rpz/ns2/base-tld2s.db | 26 + bin/tests/system/rpz/ns2/bl.tld2.db.in | 21 + bin/tests/system/rpz/ns2/blv2.tld2.db.in | 19 + bin/tests/system/rpz/ns2/blv3.tld2.db.in | 21 + bin/tests/system/rpz/ns2/hints | 13 + bin/tests/system/rpz/ns2/named.conf.in | 55 + bin/tests/system/rpz/ns2/stub.db | 20 + bin/tests/system/rpz/ns2/tld2.db | 125 + bin/tests/system/rpz/ns3/base.db | 24 + bin/tests/system/rpz/ns3/broken.db.in | 18 + bin/tests/system/rpz/ns3/crash1 | 18 + bin/tests/system/rpz/ns3/crash2 | 24 + bin/tests/system/rpz/ns3/hints | 13 + bin/tests/system/rpz/ns3/manual-update-rpz-2.db.in | 22 + bin/tests/system/rpz/ns3/manual-update-rpz.db.in | 21 + bin/tests/system/rpz/ns3/mixed-case-rpz-1.db.in | 16 + bin/tests/system/rpz/ns3/mixed-case-rpz-2.db.in | 17 + bin/tests/system/rpz/ns3/named.conf.in | 160 + bin/tests/system/rpz/ns4/hints | 13 + bin/tests/system/rpz/ns4/named.conf.in | 45 + bin/tests/system/rpz/ns4/tld4.db | 66 + bin/tests/system/rpz/ns5/empty.db.in | 14 + bin/tests/system/rpz/ns5/expire.conf.in | 19 + bin/tests/system/rpz/ns5/fast-expire.db.in | 18 + bin/tests/system/rpz/ns5/hints | 13 + bin/tests/system/rpz/ns5/named.args | 2 + bin/tests/system/rpz/ns5/named.conf.in | 91 + bin/tests/system/rpz/ns5/tld5.db | 32 + bin/tests/system/rpz/ns6/bl.tld2s.db.in | 20 + bin/tests/system/rpz/ns6/hints | 13 + bin/tests/system/rpz/ns6/named.conf.in | 67 + bin/tests/system/rpz/ns7/hints | 13 + bin/tests/system/rpz/ns7/named.conf.in | 59 + bin/tests/system/rpz/ns8/hints | 13 + bin/tests/system/rpz/ns8/manual-update-rpz.db.in | 21 + bin/tests/system/rpz/ns8/named.conf.in | 66 + bin/tests/system/rpz/ns9/hints | 13 + bin/tests/system/rpz/ns9/named.conf.in | 60 + bin/tests/system/rpz/ns9/rpz.db | 16 + bin/tests/system/rpz/qperf.sh | 22 + bin/tests/system/rpz/setup.sh | 179 + bin/tests/system/rpz/test1 | 99 + bin/tests/system/rpz/test2 | 77 + bin/tests/system/rpz/test3 | 47 + bin/tests/system/rpz/test4 | 36 + bin/tests/system/rpz/test4a | 27 + bin/tests/system/rpz/test5 | 60 + bin/tests/system/rpz/test6 | 37 + bin/tests/system/rpz/tests.sh | 1020 + bin/tests/system/rpz/tests_sh_rpz.py | 14 + bin/tests/system/rpzextra/clean.sh | 21 + bin/tests/system/rpzextra/ns2/allowed.db | 18 + bin/tests/system/rpzextra/ns2/baddomain.db | 27 + bin/tests/system/rpzextra/ns2/gooddomain.db | 27 + bin/tests/system/rpzextra/ns2/named.conf.in | 57 + .../system/rpzextra/ns2/rpz-external.local.db | 26 + .../system/rpzextra/ns3/external-rpz.local.db | 29 + bin/tests/system/rpzextra/ns3/first-rpz.local.db | 29 + .../system/rpzextra/ns3/fourth-rpz-extra.local.db | 32 + bin/tests/system/rpzextra/ns3/named.args | 1 + bin/tests/system/rpzextra/ns3/named.conf.in | 147 + bin/tests/system/rpzextra/ns3/root.db | 30 + .../system/rpzextra/ns3/third-rpz-extra.local.db | 26 + bin/tests/system/rpzextra/setup.sh | 21 + bin/tests/system/rpzextra/tests_rpzextra.py | 143 + bin/tests/system/rpzrecurse/README | 124 + bin/tests/system/rpzrecurse/ans5/ans.pl | 81 + bin/tests/system/rpzrecurse/clean.sh | 34 + bin/tests/system/rpzrecurse/ns1/db.l0 | 17 + bin/tests/system/rpzrecurse/ns1/db.l1.l0 | 17 + bin/tests/system/rpzrecurse/ns1/example.com.db | 18 + bin/tests/system/rpzrecurse/ns1/example.db | 16 + bin/tests/system/rpzrecurse/ns1/named.conf.in | 75 + bin/tests/system/rpzrecurse/ns1/root.db | 24 + .../system/rpzrecurse/ns1/test1.example.net.db | 17 + .../system/rpzrecurse/ns1/test2.example.net.db | 17 + bin/tests/system/rpzrecurse/ns2/db.clientip1 | 17 + bin/tests/system/rpzrecurse/ns2/db.clientip2 | 16 + bin/tests/system/rpzrecurse/ns2/db.clientip21 | 17 + bin/tests/system/rpzrecurse/ns2/db.given | 21 + .../system/rpzrecurse/ns2/db.invalidprefixlength | 16 + bin/tests/system/rpzrecurse/ns2/db.log1 | 16 + bin/tests/system/rpzrecurse/ns2/db.log2 | 17 + bin/tests/system/rpzrecurse/ns2/db.log3 | 18 + bin/tests/system/rpzrecurse/ns2/db.passthru | 20 + bin/tests/system/rpzrecurse/ns2/db.wildcard1 | 17 + bin/tests/system/rpzrecurse/ns2/db.wildcard2a | 17 + bin/tests/system/rpzrecurse/ns2/db.wildcard2b | 17 + bin/tests/system/rpzrecurse/ns2/db.wildcard3 | 16 + .../system/rpzrecurse/ns2/named.clientip.conf | 37 + .../system/rpzrecurse/ns2/named.clientip2.conf | 37 + .../system/rpzrecurse/ns2/named.conf.header.in | 41 + bin/tests/system/rpzrecurse/ns2/named.default.conf | 25 + .../rpzrecurse/ns2/named.invalidprefixlength.conf | 30 + bin/tests/system/rpzrecurse/ns2/named.log.conf | 39 + bin/tests/system/rpzrecurse/ns2/named.max.conf | 161 + .../system/rpzrecurse/ns2/named.wildcard1.conf | 35 + .../system/rpzrecurse/ns2/named.wildcard2.conf | 37 + .../system/rpzrecurse/ns2/named.wildcard3.conf | 35 + .../system/rpzrecurse/ns2/named.wildcard4.conf | 37 + bin/tests/system/rpzrecurse/ns2/root.hint | 14 + bin/tests/system/rpzrecurse/ns3/example.db | 17 + bin/tests/system/rpzrecurse/ns3/named1.conf.in | 43 + bin/tests/system/rpzrecurse/ns3/named2.conf.in | 42 + bin/tests/system/rpzrecurse/ns3/named3.conf.in | 40 + bin/tests/system/rpzrecurse/ns3/policy.db | 15 + bin/tests/system/rpzrecurse/ns3/root.db | 17 + bin/tests/system/rpzrecurse/ns4/child.example.db | 18 + bin/tests/system/rpzrecurse/ns4/named.conf.in | 38 + bin/tests/system/rpzrecurse/setup.sh | 87 + bin/tests/system/rpzrecurse/testgen.pl | 343 + bin/tests/system/rpzrecurse/tests.sh | 563 + bin/tests/system/rpzrecurse/tests_sh_rpzrecurse.py | 14 + bin/tests/system/rrchecker/classlist.good | 3 + bin/tests/system/rrchecker/clean.sh | 15 + bin/tests/system/rrchecker/privatelist.good | 0 bin/tests/system/rrchecker/tests.sh | 85 + bin/tests/system/rrchecker/tests_sh_rrchecker.py | 14 + bin/tests/system/rrchecker/typelist.good | 81 + bin/tests/system/rrl/broken.conf.in | 47 + bin/tests/system/rrl/clean.sh | 23 + bin/tests/system/rrl/ns1/named.conf.in | 28 + bin/tests/system/rrl/ns1/root.db | 27 + bin/tests/system/rrl/ns2/hints | 13 + bin/tests/system/rrl/ns2/named.conf.in | 65 + bin/tests/system/rrl/ns2/tld2.db | 42 + bin/tests/system/rrl/ns3/hints | 13 + bin/tests/system/rrl/ns3/named.conf.in | 48 + bin/tests/system/rrl/ns3/tld3.db | 20 + bin/tests/system/rrl/ns4/hints | 13 + bin/tests/system/rrl/ns4/named.conf.in | 67 + bin/tests/system/rrl/ns4/tld4.db | 45 + bin/tests/system/rrl/setup.sh | 22 + bin/tests/system/rrl/tests.sh | 290 + bin/tests/system/rrl/tests_sh_rrl.py | 18 + bin/tests/system/rrsetorder/clean.sh | 23 + bin/tests/system/rrsetorder/ns1/named.conf.in | 40 + bin/tests/system/rrsetorder/ns1/root.db | 51 + bin/tests/system/rrsetorder/ns2/named.conf.in | 39 + bin/tests/system/rrsetorder/ns3/named.conf.in | 38 + bin/tests/system/rrsetorder/ns4/named.conf.in | 34 + bin/tests/system/rrsetorder/ns5/named.conf.in | 30 + .../system/rrsetorder/reference.dig.out.fixed.good | 4 + .../rrsetorder/reference.dig.out.random.good1 | 4 + .../rrsetorder/reference.dig.out.random.good10 | 4 + .../rrsetorder/reference.dig.out.random.good11 | 4 + .../rrsetorder/reference.dig.out.random.good12 | 4 + .../rrsetorder/reference.dig.out.random.good13 | 4 + .../rrsetorder/reference.dig.out.random.good14 | 4 + .../rrsetorder/reference.dig.out.random.good15 | 4 + .../rrsetorder/reference.dig.out.random.good16 | 4 + .../rrsetorder/reference.dig.out.random.good17 | 4 + .../rrsetorder/reference.dig.out.random.good18 | 4 + .../rrsetorder/reference.dig.out.random.good19 | 4 + .../rrsetorder/reference.dig.out.random.good2 | 4 + .../rrsetorder/reference.dig.out.random.good20 | 4 + .../rrsetorder/reference.dig.out.random.good21 | 4 + .../rrsetorder/reference.dig.out.random.good22 | 4 + .../rrsetorder/reference.dig.out.random.good23 | 4 + .../rrsetorder/reference.dig.out.random.good24 | 4 + .../rrsetorder/reference.dig.out.random.good3 | 4 + .../rrsetorder/reference.dig.out.random.good4 | 4 + .../rrsetorder/reference.dig.out.random.good5 | 4 + .../rrsetorder/reference.dig.out.random.good6 | 4 + .../rrsetorder/reference.dig.out.random.good7 | 4 + .../rrsetorder/reference.dig.out.random.good8 | 4 + .../rrsetorder/reference.dig.out.random.good9 | 4 + bin/tests/system/rrsetorder/setup.sh | 21 + bin/tests/system/rrsetorder/tests.sh | 558 + bin/tests/system/rrsetorder/tests_sh_rrsetorder.py | 14 + bin/tests/system/rsabigexponent/README.md | 39 + bin/tests/system/rsabigexponent/bigkey.c | 157 + bin/tests/system/rsabigexponent/clean.sh | 23 + bin/tests/system/rsabigexponent/conf/bad01.conf | 16 + bin/tests/system/rsabigexponent/conf/bad02.conf | 16 + bin/tests/system/rsabigexponent/conf/bad03.conf | 16 + bin/tests/system/rsabigexponent/conf/good01.conf | 16 + bin/tests/system/rsabigexponent/conf/good02.conf | 16 + bin/tests/system/rsabigexponent/conf/good03.conf | 16 + bin/tests/system/rsabigexponent/ns1/named.conf.in | 34 + bin/tests/system/rsabigexponent/ns1/root.db.in | 24 + bin/tests/system/rsabigexponent/ns1/sign.sh | 33 + .../rsabigexponent/ns2/Xexample.+008+51650.key | 5 + .../rsabigexponent/ns2/Xexample.+008+51650.private | 13 + .../rsabigexponent/ns2/Xexample.+008+52810.key | 2 + .../rsabigexponent/ns2/Xexample.+008+52810.private | 10 + .../system/rsabigexponent/ns2/dsset-example.in | 1 + bin/tests/system/rsabigexponent/ns2/example.db.bad | 156 + bin/tests/system/rsabigexponent/ns2/example.db.in | 23 + bin/tests/system/rsabigexponent/ns2/named.conf.in | 39 + bin/tests/system/rsabigexponent/ns2/sign.sh | 28 + bin/tests/system/rsabigexponent/ns3/named.conf.in | 35 + bin/tests/system/rsabigexponent/setup.sh | 20 + bin/tests/system/rsabigexponent/tests.sh | 58 + .../rsabigexponent/tests_sh_rsabigexponent.py | 14 + bin/tests/system/run.gdb | 1 + bin/tests/system/run.sh | 27 + bin/tests/system/runall.sh | 99 + bin/tests/system/runsequential.sh | 26 + bin/tests/system/runtime/README | 13 + bin/tests/system/runtime/clean.sh | 25 + bin/tests/system/runtime/ctrl-chars | 1 + bin/tests/system/runtime/long-cmd-line | 1 + bin/tests/system/runtime/ns2/named-alt1.conf.in | 25 + bin/tests/system/runtime/ns2/named-alt2.conf.in | 25 + bin/tests/system/runtime/ns2/named-alt3.conf.in | 26 + bin/tests/system/runtime/ns2/named-alt4.conf.in | 22 + bin/tests/system/runtime/ns2/named-alt5.conf.in | 22 + bin/tests/system/runtime/ns2/named-alt6.conf.in | 22 + bin/tests/system/runtime/ns2/named-alt7.conf.in | 20 + bin/tests/system/runtime/ns2/named-alt9.conf.in | 21 + bin/tests/system/runtime/ns2/named1.conf.in | 34 + bin/tests/system/runtime/setup.sh | 29 + bin/tests/system/runtime/tests.sh | 252 + bin/tests/system/runtime/tests_sh_runtime.py | 14 + bin/tests/system/send.pl | 33 + bin/tests/system/serve-stale/ans2/ans.pl | 331 + bin/tests/system/serve-stale/clean.sh | 22 + bin/tests/system/serve-stale/ns1/named1.conf.in | 44 + bin/tests/system/serve-stale/ns1/named2.conf.in | 44 + bin/tests/system/serve-stale/ns1/named3.conf.in | 43 + bin/tests/system/serve-stale/ns1/named4.conf.in | 49 + bin/tests/system/serve-stale/ns1/root.db | 18 + bin/tests/system/serve-stale/ns1/stale.test.db | 19 + bin/tests/system/serve-stale/ns3/named1.conf.in | 41 + bin/tests/system/serve-stale/ns3/named2.conf.in | 51 + bin/tests/system/serve-stale/ns3/named3.conf.in | 48 + bin/tests/system/serve-stale/ns3/named4.conf.in | 50 + bin/tests/system/serve-stale/ns3/named5.conf.in | 49 + bin/tests/system/serve-stale/ns3/named6.conf.in | 46 + bin/tests/system/serve-stale/ns3/named7.conf.in | 55 + bin/tests/system/serve-stale/ns3/named8.conf.in | 47 + bin/tests/system/serve-stale/ns3/root.db | 13 + bin/tests/system/serve-stale/ns4/named.conf.in | 42 + bin/tests/system/serve-stale/ns5/named.conf.in | 43 + bin/tests/system/serve-stale/setup.sh | 21 + bin/tests/system/serve-stale/tests.sh | 2705 +++ .../system/serve-stale/tests_sh_serve_stale.py | 14 + bin/tests/system/setup.sh | 33 + bin/tests/system/sfcache/README | 19 + bin/tests/system/sfcache/clean.sh | 27 + bin/tests/system/sfcache/ns1/named.conf.in | 34 + bin/tests/system/sfcache/ns1/root.db.in | 26 + bin/tests/system/sfcache/ns1/sign.sh | 38 + bin/tests/system/sfcache/ns2/example.db.in | 103 + bin/tests/system/sfcache/ns2/named.conf.in | 49 + bin/tests/system/sfcache/ns2/sign.sh | 28 + bin/tests/system/sfcache/ns5/named.conf.in | 43 + bin/tests/system/sfcache/ns5/sign.sh | 21 + bin/tests/system/sfcache/setup.sh | 24 + bin/tests/system/sfcache/tests.sh | 107 + bin/tests/system/sfcache/tests_sh_sfcache.py | 14 + bin/tests/system/shutdown/clean.sh | 20 + bin/tests/system/shutdown/ns1/named.conf.in | 42 + bin/tests/system/shutdown/ns1/root.db | 25 + bin/tests/system/shutdown/ns2/named.conf.in | 40 + bin/tests/system/shutdown/ns2/test.db | 18 + bin/tests/system/shutdown/resolver/named.conf.in | 48 + bin/tests/system/shutdown/resolver/root.db | 21 + bin/tests/system/shutdown/setup.sh | 22 + bin/tests/system/shutdown/tests_shutdown.py | 206 + bin/tests/system/smartsign/child.db | 24 + bin/tests/system/smartsign/clean.sh | 15 + bin/tests/system/smartsign/parent.db | 31 + bin/tests/system/smartsign/tests.sh | 369 + bin/tests/system/smartsign/tests_sh_smartsign.py | 14 + bin/tests/system/sortlist/clean.sh | 19 + bin/tests/system/sortlist/ns1/example.db | 37 + bin/tests/system/sortlist/ns1/named.conf.in | 46 + bin/tests/system/sortlist/ns1/root.db | 24 + bin/tests/system/sortlist/setup.sh | 16 + bin/tests/system/sortlist/tests.sh | 52 + bin/tests/system/sortlist/tests_sh_sortlist.py | 14 + bin/tests/system/spf/clean.sh | 18 + bin/tests/system/spf/ns1/named.conf.in | 43 + bin/tests/system/spf/ns1/spf.db | 18 + bin/tests/system/spf/setup.sh | 16 + bin/tests/system/spf/tests.sh | 47 + bin/tests/system/spf/tests_sh_spf.py | 14 + bin/tests/system/start.pl | 452 + bin/tests/system/start.sh.in | 20 + bin/tests/system/staticstub/clean.sh | 28 + bin/tests/system/staticstub/conf/bad01.conf | 32 + bin/tests/system/staticstub/conf/bad02.conf | 32 + bin/tests/system/staticstub/conf/bad03.conf | 32 + bin/tests/system/staticstub/conf/bad04.conf | 32 + bin/tests/system/staticstub/conf/bad05.conf | 33 + bin/tests/system/staticstub/conf/bad06.conf | 33 + bin/tests/system/staticstub/conf/bad07.conf | 33 + bin/tests/system/staticstub/conf/bad08.conf | 33 + bin/tests/system/staticstub/conf/bad09.conf | 32 + bin/tests/system/staticstub/conf/bad10.conf | 34 + bin/tests/system/staticstub/conf/bad11.conf | 34 + bin/tests/system/staticstub/conf/good01.conf | 33 + bin/tests/system/staticstub/conf/good02.conf | 32 + bin/tests/system/staticstub/conf/good03.conf | 32 + bin/tests/system/staticstub/conf/good04.conf | 32 + bin/tests/system/staticstub/conf/good05.conf | 33 + bin/tests/system/staticstub/knowngood.dig.out.rec | 18 + bin/tests/system/staticstub/ns1/named.conf.in | 25 + bin/tests/system/staticstub/ns1/root.db | 19 + bin/tests/system/staticstub/ns2/named.conf.in | 62 + bin/tests/system/staticstub/ns3/example.db.in | 32 + bin/tests/system/staticstub/ns3/example.org.db | 24 + bin/tests/system/staticstub/ns3/named.conf.in | 46 + bin/tests/system/staticstub/ns3/sign.sh | 43 + bin/tests/system/staticstub/ns3/undelegated.db.in | 23 + bin/tests/system/staticstub/ns4/example.com.db | 23 + bin/tests/system/staticstub/ns4/example.info.db | 24 + bin/tests/system/staticstub/ns4/example.org.db | 25 + bin/tests/system/staticstub/ns4/named.conf.in | 45 + bin/tests/system/staticstub/ns4/sign.sh | 25 + bin/tests/system/staticstub/ns4/sub.example.db.in | 26 + bin/tests/system/staticstub/setup.sh | 25 + bin/tests/system/staticstub/tests.sh | 219 + bin/tests/system/staticstub/tests_sh_staticstub.py | 14 + bin/tests/system/statistics/ans4/ans.pl | 118 + bin/tests/system/statistics/clean.sh | 32 + bin/tests/system/statistics/ns1/named.conf.in | 44 + bin/tests/system/statistics/ns1/root.db | 24 + bin/tests/system/statistics/ns1/zone.db | 14 + bin/tests/system/statistics/ns2/example.db | 28 + bin/tests/system/statistics/ns2/internal.db | 30 + bin/tests/system/statistics/ns2/named.conf.in | 50 + bin/tests/system/statistics/ns2/named2.conf.in | 51 + bin/tests/system/statistics/ns3/internal.db | 28 + bin/tests/system/statistics/ns3/named.conf.in | 58 + bin/tests/system/statistics/ns3/root.hint | 21 + bin/tests/system/statistics/setup.sh | 18 + bin/tests/system/statistics/tests.sh | 282 + bin/tests/system/statistics/tests_sh_statistics.py | 14 + bin/tests/system/statschannel/clean.sh | 35 + bin/tests/system/statschannel/conftest.py | 25 + bin/tests/system/statschannel/fetch.pl | 43 + bin/tests/system/statschannel/generic.py | 108 + bin/tests/system/statschannel/generic_dnspython.py | 128 + bin/tests/system/statschannel/mem-xml.pl | 21 + bin/tests/system/statschannel/ns1/example.db | 49 + bin/tests/system/statschannel/ns1/named.conf.in | 44 + bin/tests/system/statschannel/ns2/dnssec.db.in | 28 + bin/tests/system/statschannel/ns2/example.db | 49 + bin/tests/system/statschannel/ns2/manykeys.db.in | 28 + bin/tests/system/statschannel/ns2/named.conf.in | 73 + bin/tests/system/statschannel/ns2/named2.conf.in | 69 + bin/tests/system/statschannel/ns2/sign.sh | 45 + bin/tests/system/statschannel/ns3/named.conf.in | 44 + bin/tests/system/statschannel/server-json.pl | 35 + bin/tests/system/statschannel/server-xml.pl | 25 + bin/tests/system/statschannel/setup.sh | 21 + bin/tests/system/statschannel/tests.sh | 656 + bin/tests/system/statschannel/tests_json.py | 105 + .../system/statschannel/tests_sh_statschannel.py | 14 + bin/tests/system/statschannel/tests_xml.py | 135 + bin/tests/system/statschannel/traffic-json.pl | 49 + bin/tests/system/statschannel/traffic-xml.pl | 46 + bin/tests/system/statschannel/traffic.expect.1 | 2 + bin/tests/system/statschannel/traffic.expect.2 | 4 + bin/tests/system/statschannel/traffic.expect.4 | 5 + bin/tests/system/statschannel/traffic.expect.5 | 7 + bin/tests/system/statschannel/traffic.expect.6 | 8 + bin/tests/system/statschannel/zones-json.pl | 37 + bin/tests/system/statschannel/zones-xml.pl | 40 + bin/tests/system/stop.pl | 281 + bin/tests/system/stop.sh.in | 22 + bin/tests/system/stopall.sh | 23 + bin/tests/system/stress/clean.sh | 22 + bin/tests/system/stress/ns2/named.conf.in | 57 + bin/tests/system/stress/ns2/zone.template.db | 21 + bin/tests/system/stress/ns3/named.conf.in | 74 + bin/tests/system/stress/ns4/named.conf.in | 57 + bin/tests/system/stress/setup.sh | 25 + bin/tests/system/stress/tests_stress_update.py | 79 + bin/tests/system/stub/clean.sh | 23 + bin/tests/system/stub/knowngood.dig.out.norec | 21 + bin/tests/system/stub/knowngood.dig.out.rec | 18 + bin/tests/system/stub/ns1/named.conf.in | 31 + bin/tests/system/stub/ns1/root.db | 24 + bin/tests/system/stub/ns2/child.example.db | 22 + bin/tests/system/stub/ns2/named.conf.in | 36 + bin/tests/system/stub/ns3/example.db | 22 + bin/tests/system/stub/ns3/named.conf.in | 42 + bin/tests/system/stub/ns4/example.db | 23 + bin/tests/system/stub/ns4/named.conf.in | 31 + bin/tests/system/stub/ns5/named.conf.in | 34 + bin/tests/system/stub/setup.sh | 20 + bin/tests/system/stub/tests.sh | 88 + bin/tests/system/stub/tests_sh_stub.py | 14 + bin/tests/system/synthfromdnssec/clean.sh | 50 + bin/tests/system/synthfromdnssec/ns1/dnamed.db.in | 17 + bin/tests/system/synthfromdnssec/ns1/example.db.in | 25 + bin/tests/system/synthfromdnssec/ns1/minimal.db.in | 46 + bin/tests/system/synthfromdnssec/ns1/named.conf.in | 72 + bin/tests/system/synthfromdnssec/ns1/root.db.in | 24 + bin/tests/system/synthfromdnssec/ns1/sign.sh | 76 + .../synthfromdnssec/ns1/soa-without-dnskey.db.in | 23 + .../system/synthfromdnssec/ns2/example.internal.db | 16 + bin/tests/system/synthfromdnssec/ns2/named.conf.in | 57 + bin/tests/system/synthfromdnssec/ns2/root.hints | 13 + bin/tests/system/synthfromdnssec/ns3/named.conf.in | 52 + bin/tests/system/synthfromdnssec/ns3/redirect.db | 20 + bin/tests/system/synthfromdnssec/ns3/root.hints | 13 + bin/tests/system/synthfromdnssec/ns4/named.conf.in | 48 + bin/tests/system/synthfromdnssec/ns4/root.hints | 13 + bin/tests/system/synthfromdnssec/ns5/internal2.db | 17 + bin/tests/system/synthfromdnssec/ns5/named.conf.in | 60 + bin/tests/system/synthfromdnssec/ns5/root.hints | 13 + bin/tests/system/synthfromdnssec/ns6/named.conf.in | 48 + bin/tests/system/synthfromdnssec/ns6/root.hints | 13 + bin/tests/system/synthfromdnssec/setup.sh | 29 + bin/tests/system/synthfromdnssec/tests.sh | 904 + .../synthfromdnssec/tests_sh_synthfromdnssec.py | 14 + .../system/tcp/1996-alloc_dnsbuf-crash-test.pkt | 12 + bin/tests/system/tcp/ans6/ans.py | 157 + bin/tests/system/tcp/clean.sh | 22 + bin/tests/system/tcp/ns1/named.conf.in | 40 + bin/tests/system/tcp/ns1/root.db | 24 + bin/tests/system/tcp/ns2/example.db | 28 + bin/tests/system/tcp/ns2/named.conf.in | 47 + bin/tests/system/tcp/ns3/named.conf.in | 42 + bin/tests/system/tcp/ns4/named.conf.in | 44 + bin/tests/system/tcp/ns5/named.conf.in | 45 + bin/tests/system/tcp/ns7/named.conf.in | 42 + bin/tests/system/tcp/ns7/named.dropedns | 1 + bin/tests/system/tcp/ns7/root.db | 24 + bin/tests/system/tcp/setup.sh | 23 + bin/tests/system/tcp/tests.sh | 203 + bin/tests/system/tcp/tests_sh_tcp.py | 14 + bin/tests/system/tcp/tests_tcp.py | 116 + bin/tests/system/testcrypto.sh | 95 + bin/tests/system/testsock.pl | 55 + bin/tests/system/testsock6.pl | 25 + bin/tests/system/testsummary.sh | 91 + bin/tests/system/timeouts/clean.sh | 21 + bin/tests/system/timeouts/ns1/example.db | 25 + bin/tests/system/timeouts/ns1/named.args | 1 + bin/tests/system/timeouts/ns1/named.conf.in | 47 + bin/tests/system/timeouts/ns1/root.db | 24 + bin/tests/system/timeouts/prereq.sh | 30 + bin/tests/system/timeouts/setup.sh | 30 + bin/tests/system/timeouts/tests_tcp_timeouts.py | 279 + bin/tests/system/tkey/clean.sh | 26 + bin/tests/system/tkey/keycreate.c | 278 + bin/tests/system/tkey/keydelete.c | 221 + bin/tests/system/tkey/ns1/example.db | 27 + bin/tests/system/tkey/ns1/named.conf.in | 50 + bin/tests/system/tkey/ns1/setup.sh | 20 + bin/tests/system/tkey/setup.sh | 20 + bin/tests/system/tkey/tests.sh | 163 + bin/tests/system/tkey/tests_sh_tkey.py | 14 + bin/tests/system/tools/clean.sh | 17 + bin/tests/system/tools/setup.sh | 17 + bin/tests/system/tools/tests.sh | 107 + bin/tests/system/tools/tests_sh_tools.py | 14 + bin/tests/system/transport-acl/clean.sh | 24 + bin/tests/system/transport-acl/ns1/named.conf.in | 129 + .../system/transport-acl/self-signed-cert.pem | 28 + bin/tests/system/transport-acl/self-signed-key.pem | 40 + bin/tests/system/transport-acl/setup.sh | 21 + bin/tests/system/transport-acl/tests.sh | 124 + .../system/transport-acl/tests_sh_transport_acl.py | 14 + bin/tests/system/tsig/ans2/ans.pl | 52 + bin/tests/system/tsig/badlocation | 37 + bin/tests/system/tsig/badtime | 37 + bin/tests/system/tsig/clean.sh | 26 + bin/tests/system/tsig/ns1/example.db | 163 + .../ns1/legacy/Khmac-md5-legacy.+157+22023.key | 1 + .../ns1/legacy/Khmac-md5-legacy.+157+22023.private | 7 + .../ns1/legacy/Khmac-sha1-legacy.+161+50591.key | 1 + .../legacy/Khmac-sha1-legacy.+161+50591.private | 7 + .../ns1/legacy/Khmac-sha224-legacy.+162+50865.key | 1 + .../legacy/Khmac-sha224-legacy.+162+50865.private | 7 + .../ns1/legacy/Khmac-sha256-legacy.+163+38999.key | 1 + .../legacy/Khmac-sha256-legacy.+163+38999.private | 7 + .../ns1/legacy/Khmac-sha384-legacy.+164+56610.key | 1 + .../legacy/Khmac-sha384-legacy.+164+56610.private | 7 + .../ns1/legacy/Khmac-sha512-legacy.+165+22767.key | 1 + .../legacy/Khmac-sha512-legacy.+165+22767.private | 7 + bin/tests/system/tsig/ns1/named.conf.in | 120 + bin/tests/system/tsig/setup.sh | 39 + bin/tests/system/tsig/tests.sh | 322 + bin/tests/system/tsig/tests_sh_tsig.py | 14 + bin/tests/system/tsiggss/authsock.pl | 91 + bin/tests/system/tsiggss/clean.sh | 28 + bin/tests/system/tsiggss/ns1/administrator.ccache | Bin 0 -> 2315 bytes bin/tests/system/tsiggss/ns1/dns.keytab | Bin 0 -> 1087 bytes bin/tests/system/tsiggss/ns1/example.nil.db.in | 62 + bin/tests/system/tsiggss/ns1/named.conf.in | 50 + bin/tests/system/tsiggss/ns1/testdenied.ccache | Bin 0 -> 2188 bytes bin/tests/system/tsiggss/prereq.sh | 22 + bin/tests/system/tsiggss/setup.sh | 21 + bin/tests/system/tsiggss/tests.sh | 178 + bin/tests/system/tsiggss/tests_isc_spnego_flaws.py | 219 + bin/tests/system/tsiggss/tests_sh_tsiggss.py | 14 + bin/tests/system/ttl/clean.sh | 17 + bin/tests/system/ttl/ns1/max-example.db | 20 + bin/tests/system/ttl/ns1/min-example.db | 20 + bin/tests/system/ttl/ns1/named.conf.in | 49 + bin/tests/system/ttl/ns2/hints.db | 13 + bin/tests/system/ttl/ns2/named.conf.in | 42 + bin/tests/system/ttl/setup.sh | 17 + bin/tests/system/ttl/tests_cache_ttl.py | 32 + bin/tests/system/unknown/clean.sh | 22 + bin/tests/system/unknown/large.out | 1 + bin/tests/system/unknown/ns1/broken1.db | 23 + bin/tests/system/unknown/ns1/broken2.db | 23 + bin/tests/system/unknown/ns1/broken3.db | 23 + bin/tests/system/unknown/ns1/broken4.db | 23 + bin/tests/system/unknown/ns1/broken5.db | 23 + bin/tests/system/unknown/ns1/class10.hints | 13 + bin/tests/system/unknown/ns1/example-class10.db | 31 + bin/tests/system/unknown/ns1/example-in.db | 56 + bin/tests/system/unknown/ns1/large.db | 3011 +++ bin/tests/system/unknown/ns1/named.conf.in | 69 + bin/tests/system/unknown/ns2/named.conf.in | 33 + bin/tests/system/unknown/ns3/named.conf.in | 35 + bin/tests/system/unknown/ns3/sign.sh | 20 + bin/tests/system/unknown/setup.sh | 20 + bin/tests/system/unknown/tests.sh | 235 + bin/tests/system/unknown/tests_sh_unknown.py | 14 + bin/tests/system/unknown/zones/nan.bad | 12 + bin/tests/system/upforwd/ans4/ans.pl | 363 + bin/tests/system/upforwd/clean.sh | 35 + bin/tests/system/upforwd/knowngood.after1 | 10 + bin/tests/system/upforwd/knowngood.after2 | 11 + bin/tests/system/upforwd/knowngood.before | 8 + bin/tests/system/upforwd/knowngood.ns2.before | 6 + bin/tests/system/upforwd/ns1/example1.db | 18 + bin/tests/system/upforwd/ns1/named.conf.in | 42 + bin/tests/system/upforwd/ns2/named.conf.in | 37 + bin/tests/system/upforwd/ns3/named1.conf.in | 64 + bin/tests/system/upforwd/ns3/named2.conf.in | 44 + bin/tests/system/upforwd/ns3/noprimary.db | 14 + bin/tests/system/upforwd/setup.sh | 47 + bin/tests/system/upforwd/tests.sh | 295 + bin/tests/system/upforwd/tests_sh_upforwd.py | 14 + bin/tests/system/verify/clean.sh | 21 + bin/tests/system/verify/setup.sh | 16 + bin/tests/system/verify/tests.sh | 113 + bin/tests/system/verify/tests_sh_verify.py | 14 + bin/tests/system/verify/zones/genzones.sh | 247 + bin/tests/system/verify/zones/unsigned.db | 29 + bin/tests/system/views/clean.sh | 38 + bin/tests/system/views/ns1/named.conf.in | 30 + bin/tests/system/views/ns1/root.db | 24 + bin/tests/system/views/ns2/1.10.in-addr.arpa.db | 13 + bin/tests/system/views/ns2/clone.db | 25 + bin/tests/system/views/ns2/example1.db | 28 + bin/tests/system/views/ns2/example2.db | 28 + bin/tests/system/views/ns2/external/inline.db | 29 + bin/tests/system/views/ns2/internal.db | 30 + bin/tests/system/views/ns2/internal/inline.db | 29 + bin/tests/system/views/ns2/named1.conf.in | 55 + bin/tests/system/views/ns2/named2.conf.in | 103 + bin/tests/system/views/ns2/named3.conf.in | 36 + bin/tests/system/views/ns3/child.clone.db | 22 + bin/tests/system/views/ns3/internal.db | 28 + bin/tests/system/views/ns3/named1.conf.in | 51 + bin/tests/system/views/ns3/named2.conf.in | 51 + bin/tests/system/views/ns5/child.clone.db | 22 + bin/tests/system/views/ns5/named.conf.in | 45 + bin/tests/system/views/setup.sh | 38 + bin/tests/system/views/tests.sh | 189 + bin/tests/system/views/tests_sh_views.py | 14 + bin/tests/system/wildcard/clean.sh | 28 + bin/tests/system/wildcard/ns1/allwild.db.in | 15 + bin/tests/system/wildcard/ns1/dlv.db.in | 14 + bin/tests/system/wildcard/ns1/example.db.in | 25 + bin/tests/system/wildcard/ns1/named.conf.in | 43 + bin/tests/system/wildcard/ns1/nsec.db.in | 17 + bin/tests/system/wildcard/ns1/nsec3.db.in | 17 + bin/tests/system/wildcard/ns1/private.nsec.db.in | 16 + bin/tests/system/wildcard/ns1/private.nsec3.db.in | 17 + bin/tests/system/wildcard/ns1/root.db.in | 17 + bin/tests/system/wildcard/ns1/sign.sh | 95 + bin/tests/system/wildcard/ns2/named.conf.in | 30 + bin/tests/system/wildcard/ns3/named.conf.in | 32 + bin/tests/system/wildcard/ns4/named.conf.in | 31 + bin/tests/system/wildcard/ns5/named.conf.in | 32 + bin/tests/system/wildcard/setup.sh | 22 + bin/tests/system/wildcard/tests.sh | 282 + bin/tests/system/wildcard/tests_sh_wildcard.py | 14 + bin/tests/system/wildcard/tests_wildcard.py | 112 + bin/tests/system/xfer/ans5/badkeydata | 10 + bin/tests/system/xfer/ans5/badmessageid | 10 + bin/tests/system/xfer/ans5/goodaxfr | 10 + bin/tests/system/xfer/ans5/partial | 11 + bin/tests/system/xfer/ans5/soamismatch | 10 + bin/tests/system/xfer/ans5/unknownkey | 11 + bin/tests/system/xfer/ans5/unsigned | 11 + bin/tests/system/xfer/ans5/wrongkey | 11 + bin/tests/system/xfer/ans5/wrongname | 10 + bin/tests/system/xfer/axfr-stats.good | 3 + bin/tests/system/xfer/clean.sh | 41 + bin/tests/system/xfer/dig1.good | 178 + bin/tests/system/xfer/dig2.good | 178 + bin/tests/system/xfer/dig3.good | 6 + bin/tests/system/xfer/knowngood.mapped | 26 + bin/tests/system/xfer/ns1/axfr-max-idle-time.db | 15 + .../system/xfer/ns1/axfr-max-transfer-time.db | 15 + bin/tests/system/xfer/ns1/axfr-too-big.db | 15 + bin/tests/system/xfer/ns1/dot-fallback.db.in | 19 + bin/tests/system/xfer/ns1/ixfr-too-big.db.in | 18 + bin/tests/system/xfer/ns1/named1.conf.in | 77 + bin/tests/system/xfer/ns1/named2.conf.in | 41 + bin/tests/system/xfer/ns1/named3.conf.in | 41 + bin/tests/system/xfer/ns1/root.db | 27 + bin/tests/system/xfer/ns1/xfer-stats.db | 15 + bin/tests/system/xfer/ns2/mapped.db.in | 28 + bin/tests/system/xfer/ns2/named.conf.in | 84 + bin/tests/system/xfer/ns2/sec.db.in | 19 + bin/tests/system/xfer/ns3/named.conf.in | 81 + bin/tests/system/xfer/ns4/named.conf.base | 49 + bin/tests/system/xfer/ns4/root.db.in | 14 + bin/tests/system/xfer/ns6/named.args | 1 + bin/tests/system/xfer/ns6/named.conf.in | 85 + bin/tests/system/xfer/ns7/named.conf.in | 55 + bin/tests/system/xfer/ns8/example.db | 24 + bin/tests/system/xfer/ns8/named.conf.in | 47 + bin/tests/system/xfer/prereq.sh | 26 + bin/tests/system/xfer/setup.sh | 45 + bin/tests/system/xfer/tests.sh | 627 + bin/tests/system/xfer/tests_sh_xfer.py | 14 + bin/tests/system/xferquota/clean.sh | 26 + bin/tests/system/xferquota/ns1/changing1.db | 27 + bin/tests/system/xferquota/ns1/changing2.db | 27 + bin/tests/system/xferquota/ns1/named.conf.in | 46 + bin/tests/system/xferquota/ns1/root.db | 29 + bin/tests/system/xferquota/ns2/example.db | 146 + bin/tests/system/xferquota/ns2/named.conf.in | 41 + bin/tests/system/xferquota/setup.pl | 40 + bin/tests/system/xferquota/setup.sh | 25 + bin/tests/system/xferquota/tests.sh | 61 + bin/tests/system/xferquota/tests_sh_xferquota.py | 14 + bin/tests/system/zero/ans5/ans.pl | 81 + bin/tests/system/zero/clean.sh | 22 + bin/tests/system/zero/ns1/named.conf.in | 29 + bin/tests/system/zero/ns1/root.db | 26 + bin/tests/system/zero/ns2/named.args | 1 + bin/tests/system/zero/ns2/named.conf.in | 34 + bin/tests/system/zero/ns2/tld.db | 20 + bin/tests/system/zero/ns3/named.args | 1 + bin/tests/system/zero/ns3/named.conf.in | 30 + bin/tests/system/zero/ns3/root.hint | 13 + bin/tests/system/zero/ns4/named.args | 1 + bin/tests/system/zero/ns4/named.conf.in | 35 + bin/tests/system/zero/ns4/one.tld.db | 17 + bin/tests/system/zero/setup.sh | 21 + bin/tests/system/zero/tests.sh | 123 + bin/tests/system/zero/tests_sh_zero.py | 14 + bin/tests/system/zonechecks/a.db | 14 + bin/tests/system/zonechecks/aaaa.db | 14 + bin/tests/system/zonechecks/bigserial.db | 14 + bin/tests/system/zonechecks/clean.sh | 22 + bin/tests/system/zonechecks/cname.db | 14 + bin/tests/system/zonechecks/dname.db | 14 + bin/tests/system/zonechecks/noaddress.db | 14 + bin/tests/system/zonechecks/ns1/named.conf.in | 72 + bin/tests/system/zonechecks/ns2/named.conf.in | 42 + bin/tests/system/zonechecks/nxdomain.db | 14 + bin/tests/system/zonechecks/setup.sh | 33 + bin/tests/system/zonechecks/tests.sh | 258 + bin/tests/system/zonechecks/tests_sh_zonechecks.py | 14 + bin/tests/test_client.c | 453 + bin/tests/test_server.c | 326 + bin/tests/wire_test.c | 353 + bin/tools/Makefile.am | 58 + bin/tools/Makefile.in | 968 + bin/tools/arpaname.c | 48 + bin/tools/arpaname.rst | 35 + bin/tools/dnstap-read.c | 428 + bin/tools/dnstap-read.rst | 58 + bin/tools/mdig.c | 2249 +++ bin/tools/mdig.rst | 375 + bin/tools/named-journalprint.c | 134 + bin/tools/named-journalprint.rst | 66 + bin/tools/named-nzd2nzf.c | 102 + bin/tools/named-nzd2nzf.rst | 45 + bin/tools/named-rrchecker.c | 346 + bin/tools/named-rrchecker.rst | 62 + bin/tools/nsec3hash.c | 194 + bin/tools/nsec3hash.rst | 70 + 2763 files changed, 306437 insertions(+) create mode 100644 bin/Makefile.am create mode 100644 bin/Makefile.in create mode 100644 bin/check/Makefile.am create mode 100644 bin/check/Makefile.in create mode 100644 bin/check/check-tool.c create mode 100644 bin/check/check-tool.h create mode 100644 bin/check/named-checkconf.c create mode 100644 bin/check/named-checkconf.rst create mode 100644 bin/check/named-checkzone.c create mode 100644 bin/check/named-checkzone.rst create mode 100644 bin/check/named-compilezone.rst create mode 100644 bin/confgen/Makefile.am create mode 100644 bin/confgen/Makefile.in create mode 100644 bin/confgen/ddns-confgen.rst create mode 100644 bin/confgen/include/confgen/os.h create mode 100644 bin/confgen/keygen.c create mode 100644 bin/confgen/keygen.h create mode 100644 bin/confgen/os.c create mode 100644 bin/confgen/rndc-confgen.c create mode 100644 bin/confgen/rndc-confgen.rst create mode 100644 bin/confgen/tsig-keygen.c create mode 100644 bin/confgen/tsig-keygen.rst create mode 100644 bin/confgen/util.c create mode 100644 bin/confgen/util.h create mode 100644 bin/delv/Makefile.am create mode 100644 bin/delv/Makefile.in create mode 100644 bin/delv/delv.c create mode 100644 bin/delv/delv.rst create mode 100644 bin/dig/Makefile.am create mode 100644 bin/dig/Makefile.in create mode 100644 bin/dig/dig.c create mode 100644 bin/dig/dig.rst create mode 100644 bin/dig/dighost.c create mode 100644 bin/dig/dighost.h create mode 100644 bin/dig/host.c create mode 100644 bin/dig/host.rst create mode 100644 bin/dig/nslookup.c create mode 100644 bin/dig/nslookup.rst create mode 100644 bin/dig/readline.h create mode 100644 bin/dnssec/Makefile.am create mode 100644 bin/dnssec/Makefile.in create mode 100644 bin/dnssec/dnssec-cds.c create mode 100644 bin/dnssec/dnssec-cds.rst create mode 100644 bin/dnssec/dnssec-dsfromkey.c create mode 100644 bin/dnssec/dnssec-dsfromkey.rst create mode 100644 bin/dnssec/dnssec-importkey.c create mode 100644 bin/dnssec/dnssec-importkey.rst create mode 100644 bin/dnssec/dnssec-keyfromlabel.c create mode 100644 bin/dnssec/dnssec-keyfromlabel.rst create mode 100644 bin/dnssec/dnssec-keygen.c create mode 100644 bin/dnssec/dnssec-keygen.rst create mode 100644 bin/dnssec/dnssec-revoke.c create mode 100644 bin/dnssec/dnssec-revoke.rst create mode 100644 bin/dnssec/dnssec-settime.c create mode 100644 bin/dnssec/dnssec-settime.rst create mode 100644 bin/dnssec/dnssec-signzone.c create mode 100644 bin/dnssec/dnssec-signzone.rst create mode 100644 bin/dnssec/dnssec-verify.c create mode 100644 bin/dnssec/dnssec-verify.rst create mode 100644 bin/dnssec/dnssectool.c create mode 100644 bin/dnssec/dnssectool.h create mode 100644 bin/named/Makefile.am create mode 100644 bin/named/Makefile.in create mode 100644 bin/named/bind9.xsl create mode 100644 bin/named/builtin.c create mode 100644 bin/named/config.c create mode 100644 bin/named/control.c create mode 100644 bin/named/controlconf.c create mode 100644 bin/named/dlz_dlopen_driver.c create mode 100644 bin/named/fuzz.c create mode 100644 bin/named/geoip.c create mode 100644 bin/named/include/dlz/dlz_dlopen_driver.h create mode 100644 bin/named/include/named/builtin.h create mode 100644 bin/named/include/named/config.h create mode 100644 bin/named/include/named/control.h create mode 100644 bin/named/include/named/fuzz.h create mode 100644 bin/named/include/named/geoip.h create mode 100644 bin/named/include/named/globals.h create mode 100644 bin/named/include/named/log.h create mode 100644 bin/named/include/named/logconf.h create mode 100644 bin/named/include/named/main.h create mode 100644 bin/named/include/named/os.h create mode 100644 bin/named/include/named/server.h create mode 100644 bin/named/include/named/smf_globals.h create mode 100644 bin/named/include/named/statschannel.h create mode 100644 bin/named/include/named/tkeyconf.h create mode 100644 bin/named/include/named/transportconf.h create mode 100644 bin/named/include/named/tsigconf.h create mode 100644 bin/named/include/named/types.h create mode 100644 bin/named/include/named/zoneconf.h create mode 100644 bin/named/log.c create mode 100644 bin/named/logconf.c create mode 100644 bin/named/main.c create mode 100644 bin/named/named.conf.rst create mode 100644 bin/named/named.rst create mode 100644 bin/named/os.c create mode 100644 bin/named/server.c create mode 100644 bin/named/statschannel.c create mode 100644 bin/named/tkeyconf.c create mode 100644 bin/named/transportconf.c create mode 100644 bin/named/tsigconf.c create mode 100644 bin/named/xsl_p.h create mode 100644 bin/named/zoneconf.c create mode 100644 bin/nsupdate/Makefile.am create mode 100644 bin/nsupdate/Makefile.in create mode 100644 bin/nsupdate/nsupdate.c create mode 100644 bin/nsupdate/nsupdate.rst create mode 100644 bin/plugins/Makefile.am create mode 100644 bin/plugins/Makefile.in create mode 100644 bin/plugins/filter-a.c create mode 100644 bin/plugins/filter-a.rst create mode 100644 bin/plugins/filter-aaaa.c create mode 100644 bin/plugins/filter-aaaa.rst create mode 100644 bin/rndc/Makefile.am create mode 100644 bin/rndc/Makefile.in create mode 100644 bin/rndc/rndc.c create mode 100644 bin/rndc/rndc.conf.rst create mode 100644 bin/rndc/rndc.rst create mode 100644 bin/rndc/util.c create mode 100644 bin/rndc/util.h create mode 100644 bin/tests/Makefile.am create mode 100644 bin/tests/Makefile.in create mode 100755 bin/tests/convert-trs-to-junit.py create mode 100644 bin/tests/system/Makefile.am create mode 100644 bin/tests/system/Makefile.in create mode 100644 bin/tests/system/README create mode 100644 bin/tests/system/acl/clean.sh create mode 100644 bin/tests/system/acl/ns2/named1.conf.in create mode 100644 bin/tests/system/acl/ns2/named2.conf.in create mode 100644 bin/tests/system/acl/ns2/named3.conf.in create mode 100644 bin/tests/system/acl/ns2/named4.conf.in create mode 100644 bin/tests/system/acl/ns2/named5.conf.in create mode 100644 bin/tests/system/acl/ns3/example.db create mode 100644 bin/tests/system/acl/ns3/named.conf.in create mode 100644 bin/tests/system/acl/ns4/example.db create mode 100644 bin/tests/system/acl/ns4/existing.db create mode 100644 bin/tests/system/acl/ns4/named.conf.in create mode 100644 bin/tests/system/acl/setup.sh create mode 100644 bin/tests/system/acl/tests.sh create mode 100644 bin/tests/system/acl/tests_sh_acl.py create mode 100644 bin/tests/system/additional/clean.sh create mode 100644 bin/tests/system/additional/ns1/mx.db create mode 100644 bin/tests/system/additional/ns1/named.args create mode 100644 bin/tests/system/additional/ns1/named1.conf.in create mode 100644 bin/tests/system/additional/ns1/named2.conf.in create mode 100644 bin/tests/system/additional/ns1/named3.conf.in create mode 100644 bin/tests/system/additional/ns1/named4.conf.in create mode 100644 bin/tests/system/additional/ns1/naptr.db create mode 100644 bin/tests/system/additional/ns1/naptr2.db create mode 100644 bin/tests/system/additional/ns1/nid.db create mode 100644 bin/tests/system/additional/ns1/root.db create mode 100644 bin/tests/system/additional/ns1/rt.db create mode 100644 bin/tests/system/additional/ns1/rt2.db create mode 100644 bin/tests/system/additional/ns1/srv.db create mode 100644 bin/tests/system/additional/ns2/named.conf.in create mode 100644 bin/tests/system/additional/ns2/root.db create mode 100644 bin/tests/system/additional/ns3/ex.db create mode 100644 bin/tests/system/additional/ns3/ex2.db create mode 100644 bin/tests/system/additional/ns3/named.conf.in create mode 100644 bin/tests/system/additional/ns3/root.hint create mode 100644 bin/tests/system/additional/setup.sh create mode 100644 bin/tests/system/additional/tests.sh create mode 100644 bin/tests/system/additional/tests_sh_additional.py create mode 100644 bin/tests/system/addzone/clean.sh create mode 100644 bin/tests/system/addzone/ns1/inlinesec.db create mode 100644 bin/tests/system/addzone/ns1/named.conf.in create mode 100644 bin/tests/system/addzone/ns1/redirect.db.1 create mode 100644 bin/tests/system/addzone/ns1/redirect.db.2 create mode 100644 bin/tests/system/addzone/ns2/added.db create mode 100644 bin/tests/system/addzone/ns2/default.nzf.in create mode 100644 bin/tests/system/addzone/ns2/hints.db create mode 100644 bin/tests/system/addzone/ns2/inline.db create mode 100644 bin/tests/system/addzone/ns2/named1.conf.in create mode 100644 bin/tests/system/addzone/ns2/named2.conf.in create mode 100644 bin/tests/system/addzone/ns2/named3.conf.in create mode 100644 bin/tests/system/addzone/ns2/normal.db create mode 100644 bin/tests/system/addzone/ns2/previous.db create mode 100644 bin/tests/system/addzone/ns2/redirect.db.1 create mode 100644 bin/tests/system/addzone/ns2/redirect.db.2 create mode 100644 bin/tests/system/addzone/ns3/e.db create mode 100644 bin/tests/system/addzone/ns3/example.db create mode 100644 bin/tests/system/addzone/ns3/named1.conf.in create mode 100644 bin/tests/system/addzone/ns3/named2.conf.in create mode 100644 bin/tests/system/addzone/ns3/redirect.db.1 create mode 100644 bin/tests/system/addzone/ns3/redirect.db.2 create mode 100644 bin/tests/system/addzone/setup.sh create mode 100755 bin/tests/system/addzone/tests.sh create mode 100755 bin/tests/system/addzone/tests_rndc_deadlock.py create mode 100644 bin/tests/system/addzone/tests_sh_addzone.py create mode 100644 bin/tests/system/allow-query/clean.sh create mode 100644 bin/tests/system/allow-query/ns1/named.conf.in create mode 100644 bin/tests/system/allow-query/ns1/root.db create mode 100644 bin/tests/system/allow-query/ns2/generic.db create mode 100644 bin/tests/system/allow-query/ns2/named01.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named02.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named03.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named04.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named05.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named06.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named07.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named08.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named09.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named10.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named11.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named12.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named21.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named22.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named23.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named24.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named25.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named26.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named27.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named28.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named29.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named30.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named31.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named32.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named33.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named34.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named40.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named53.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named54.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named55.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named56.conf.in create mode 100644 bin/tests/system/allow-query/ns2/named57.conf.in create mode 100644 bin/tests/system/allow-query/ns3/named.args create mode 100644 bin/tests/system/allow-query/ns3/named1.conf.in create mode 100644 bin/tests/system/allow-query/ns3/named2.conf.in create mode 100644 bin/tests/system/allow-query/ns3/named3.conf.in create mode 100644 bin/tests/system/allow-query/ns3/named4.conf.in create mode 100644 bin/tests/system/allow-query/setup.sh create mode 100644 bin/tests/system/allow-query/tests.sh create mode 100644 bin/tests/system/allow-query/tests_sh_allowquery.py create mode 100644 bin/tests/system/ans.pl create mode 100644 bin/tests/system/auth/clean.sh create mode 100644 bin/tests/system/auth/ns1/chaos.db create mode 100644 bin/tests/system/auth/ns1/example.com.db create mode 100644 bin/tests/system/auth/ns1/example.net.db create mode 100644 bin/tests/system/auth/ns1/named.conf.in create mode 100644 bin/tests/system/auth/ns2/named.conf.in create mode 100644 bin/tests/system/auth/setup.sh create mode 100644 bin/tests/system/auth/tests.sh create mode 100644 bin/tests/system/auth/tests_sh_auth.py create mode 100644 bin/tests/system/autosign/clean.sh create mode 100644 bin/tests/system/autosign/ns1/keygen.sh create mode 100644 bin/tests/system/autosign/ns1/named.conf.in create mode 100644 bin/tests/system/autosign/ns1/root.db.in create mode 100644 bin/tests/system/autosign/ns2/Xbar.+013+59973.key create mode 100644 bin/tests/system/autosign/ns2/Xbar.+013+59973.private create mode 100644 bin/tests/system/autosign/ns2/Xbar.+013+60101.key create mode 100644 bin/tests/system/autosign/ns2/Xbar.+013+60101.private create mode 100644 bin/tests/system/autosign/ns2/bar.db.in create mode 100644 bin/tests/system/autosign/ns2/child.nsec3.example.db create mode 100644 bin/tests/system/autosign/ns2/child.optout.example.db create mode 100644 bin/tests/system/autosign/ns2/dst.example.db.in create mode 100644 bin/tests/system/autosign/ns2/example.db.in create mode 100644 bin/tests/system/autosign/ns2/insecure.secure.example.db create mode 100644 bin/tests/system/autosign/ns2/keygen.sh create mode 100644 bin/tests/system/autosign/ns2/named.conf.in create mode 100644 bin/tests/system/autosign/ns2/optout-with-ent.db.in create mode 100644 bin/tests/system/autosign/ns2/private.secure.example.db.in create mode 100644 bin/tests/system/autosign/ns3/autonsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/cdnskey-delete.example.db.in create mode 100644 bin/tests/system/autosign/ns3/cds-delete.example.db.in create mode 100644 bin/tests/system/autosign/ns3/delay.example.db create mode 100644 bin/tests/system/autosign/ns3/delzsk.example.db.in create mode 100644 bin/tests/system/autosign/ns3/dname-at-apex-nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/inacksk2.example.db.in create mode 100644 bin/tests/system/autosign/ns3/inacksk3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/inaczsk.example.db.in create mode 100644 bin/tests/system/autosign/ns3/inaczsk2.example.db.in create mode 100644 bin/tests/system/autosign/ns3/inaczsk3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/insecure.example.db create mode 100644 bin/tests/system/autosign/ns3/jitter.nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/keygen.sh create mode 100644 bin/tests/system/autosign/ns3/kskonly.example.db.in create mode 100644 bin/tests/system/autosign/ns3/named.conf.in create mode 100644 bin/tests/system/autosign/ns3/noksk.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nozsk.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nsec-only.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nsec3-to-nsec.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nsec3.nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/nsec3.optout.example.db.in create mode 100644 bin/tests/system/autosign/ns3/oldsigs.example.db.in create mode 100644 bin/tests/system/autosign/ns3/optout.example.db.in create mode 100644 bin/tests/system/autosign/ns3/optout.nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/optout.optout.example.db.in create mode 100644 bin/tests/system/autosign/ns3/rsasha256.example.db.in create mode 100644 bin/tests/system/autosign/ns3/rsasha512.example.db.in create mode 100644 bin/tests/system/autosign/ns3/secure-to-insecure.example.db.in create mode 100644 bin/tests/system/autosign/ns3/secure-to-insecure2.example.db.in create mode 100644 bin/tests/system/autosign/ns3/secure.example.db.in create mode 100644 bin/tests/system/autosign/ns3/secure.nsec3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/secure.optout.example.db.in create mode 100644 bin/tests/system/autosign/ns3/sync.example.db.in create mode 100644 bin/tests/system/autosign/ns3/ttl1.example.db.in create mode 100644 bin/tests/system/autosign/ns3/ttl2.example.db.in create mode 100644 bin/tests/system/autosign/ns3/ttl3.example.db.in create mode 100644 bin/tests/system/autosign/ns3/ttl4.example.db.in create mode 100644 bin/tests/system/autosign/ns4/named.conf.in create mode 100644 bin/tests/system/autosign/ns5/named.conf.in create mode 100644 bin/tests/system/autosign/setup.sh create mode 100755 bin/tests/system/autosign/tests.sh create mode 100644 bin/tests/system/autosign/tests_sh_autosign.py create mode 100644 bin/tests/system/builtin/clean.sh create mode 100644 bin/tests/system/builtin/ns1/named.conf.in create mode 100644 bin/tests/system/builtin/ns2/named.conf.in create mode 100644 bin/tests/system/builtin/ns3/named.conf.in create mode 100644 bin/tests/system/builtin/setup.sh create mode 100644 bin/tests/system/builtin/tests.sh create mode 100644 bin/tests/system/builtin/tests_sh_builtin.py create mode 100644 bin/tests/system/cacheclean/clean.sh create mode 100644 bin/tests/system/cacheclean/dig.batch create mode 100644 bin/tests/system/cacheclean/knowngood.dig.out create mode 100644 bin/tests/system/cacheclean/ns1/example.db create mode 100644 bin/tests/system/cacheclean/ns1/expire-test.db create mode 100644 bin/tests/system/cacheclean/ns1/flushtest.db create mode 100644 bin/tests/system/cacheclean/ns1/named.args create mode 100644 bin/tests/system/cacheclean/ns1/named.conf.in create mode 100644 bin/tests/system/cacheclean/ns2/named.args create mode 100644 bin/tests/system/cacheclean/ns2/named.conf.in create mode 100644 bin/tests/system/cacheclean/setup.sh create mode 100755 bin/tests/system/cacheclean/tests.sh create mode 100644 bin/tests/system/cacheclean/tests_sh_cacheclean.py create mode 100644 bin/tests/system/case/clean.sh create mode 100644 bin/tests/system/case/dynamic.good create mode 100644 bin/tests/system/case/ns1/dynamic.db.in create mode 100644 bin/tests/system/case/ns1/example.db create mode 100644 bin/tests/system/case/ns1/named.conf.in create mode 100644 bin/tests/system/case/ns2/named.conf.in create mode 100644 bin/tests/system/case/postns1.good create mode 100644 bin/tests/system/case/postupdate.good create mode 100644 bin/tests/system/case/setup.sh create mode 100644 bin/tests/system/case/tests.sh create mode 100644 bin/tests/system/case/tests_sh_case.py create mode 100644 bin/tests/system/catz/clean.sh create mode 100644 bin/tests/system/catz/ns1/catalog-bad1.example.db create mode 100644 bin/tests/system/catz/ns1/catalog-bad2.example.db create mode 100644 bin/tests/system/catz/ns1/catalog-bad3.example.db create mode 100644 bin/tests/system/catz/ns1/catalog-bad4.example.db create mode 100644 bin/tests/system/catz/ns1/catalog-bad5.example.db create mode 100644 bin/tests/system/catz/ns1/catalog.example.db.in create mode 100644 bin/tests/system/catz/ns1/named.conf.in create mode 100644 bin/tests/system/catz/ns2/dom-existing.example.db create mode 100644 bin/tests/system/catz/ns2/named1.conf.in create mode 100644 bin/tests/system/catz/ns2/named2.conf.in create mode 100644 bin/tests/system/catz/ns3/catalog.example.db.in create mode 100644 bin/tests/system/catz/ns3/dom5.example.db create mode 100644 bin/tests/system/catz/ns3/dom6.example.db create mode 100644 bin/tests/system/catz/ns3/named.conf.in create mode 100644 bin/tests/system/catz/ns4/catalog.example.db.in create mode 100644 bin/tests/system/catz/ns4/named.conf.in create mode 100644 bin/tests/system/catz/setup.sh create mode 100644 bin/tests/system/catz/tests.sh create mode 100644 bin/tests/system/catz/tests_sh_catz.py create mode 100644 bin/tests/system/cds/checkmtime.pl create mode 100644 bin/tests/system/cds/checktime.pl create mode 100644 bin/tests/system/cds/clean.sh create mode 100644 bin/tests/system/cds/mangle.pl create mode 100644 bin/tests/system/cds/setup.sh create mode 100644 bin/tests/system/cds/tests.sh create mode 100644 bin/tests/system/cds/tests_sh_cds.py create mode 100644 bin/tests/system/chain/README create mode 100644 bin/tests/system/chain/ans3/ans.pl create mode 100644 bin/tests/system/chain/ans4/README.anspy create mode 100755 bin/tests/system/chain/ans4/ans.py create mode 100755 bin/tests/system/chain/clean.sh create mode 100644 bin/tests/system/chain/ns1/named.conf.in create mode 100644 bin/tests/system/chain/ns1/root.db create mode 100644 bin/tests/system/chain/ns2/example.db create mode 100644 bin/tests/system/chain/ns2/generic.db create mode 100644 bin/tests/system/chain/ns2/named.conf.in create mode 100644 bin/tests/system/chain/ns2/sign.sh create mode 100644 bin/tests/system/chain/ns2/sub.db create mode 100644 bin/tests/system/chain/ns2/wildcard-secure.db create mode 100644 bin/tests/system/chain/ns2/wildcard.db create mode 100644 bin/tests/system/chain/ns5/named.conf.in create mode 100644 bin/tests/system/chain/ns5/sub.db create mode 100644 bin/tests/system/chain/ns7/named.conf.in create mode 100644 bin/tests/system/chain/ns7/root.hint create mode 100644 bin/tests/system/chain/setup.sh create mode 100644 bin/tests/system/chain/tests.sh create mode 100644 bin/tests/system/chain/tests_sh_chain.py create mode 100644 bin/tests/system/checkconf/altdb.conf create mode 100644 bin/tests/system/checkconf/altdlz.conf create mode 100644 bin/tests/system/checkconf/ancient.conf create mode 100644 bin/tests/system/checkconf/bad-acl.conf create mode 100644 bin/tests/system/checkconf/bad-also-notify.conf create mode 100644 bin/tests/system/checkconf/bad-catz-zone-dup.conf create mode 100644 bin/tests/system/checkconf/bad-catz-zone-primary-dup.conf create mode 100644 bin/tests/system/checkconf/bad-catz-zone.conf create mode 100644 bin/tests/system/checkconf/bad-checknames-primary-dup-2.conf create mode 100644 bin/tests/system/checkconf/bad-checknames-primary-dup.conf create mode 100644 bin/tests/system/checkconf/bad-checknames-secondary-dup.conf create mode 100644 bin/tests/system/checkconf/bad-dnskey-validity.conf create mode 100644 bin/tests/system/checkconf/bad-dnssec.conf create mode 100644 bin/tests/system/checkconf/bad-doh-1.conf create mode 100644 bin/tests/system/checkconf/bad-doh-2.conf create mode 100644 bin/tests/system/checkconf/bad-doh-3.conf create mode 100644 bin/tests/system/checkconf/bad-doh-badpath-1.conf create mode 100644 bin/tests/system/checkconf/bad-doh-badpath-2.conf create mode 100644 bin/tests/system/checkconf/bad-doh-badpath-3.conf create mode 100644 bin/tests/system/checkconf/bad-doh-default.conf create mode 100644 bin/tests/system/checkconf/bad-doh-duplicates.conf create mode 100644 bin/tests/system/checkconf/bad-dot-1.conf create mode 100644 bin/tests/system/checkconf/bad-dot-allow-transfer-bad-port.conf create mode 100644 bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-1.conf create mode 100644 bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-2.conf create mode 100644 bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-3.conf create mode 100644 bin/tests/system/checkconf/bad-dot-allow-transfer-bad-proto-4.conf create mode 100644 bin/tests/system/checkconf/bad-dot-badciphers.conf create mode 100644 bin/tests/system/checkconf/bad-dot-badprotocol.conf create mode 100644 bin/tests/system/checkconf/bad-dot-duplicatetls.conf create mode 100644 bin/tests/system/checkconf/bad-dot-ephemeral.conf create mode 100644 bin/tests/system/checkconf/bad-dot-nocert.conf create mode 100644 bin/tests/system/checkconf/bad-dot-nokey.conf create mode 100644 bin/tests/system/checkconf/bad-dot-none.conf create mode 100644 bin/tests/system/checkconf/bad-dot-primaries.conf create mode 100644 bin/tests/system/checkconf/bad-duplicate-key.conf create mode 100644 bin/tests/system/checkconf/bad-duplicate-primaries-1.conf create mode 100644 bin/tests/system/checkconf/bad-duplicate-primaries-2.conf create mode 100644 bin/tests/system/checkconf/bad-duplicate-root-key.conf create mode 100644 bin/tests/system/checkconf/bad-duration.conf create mode 100644 bin/tests/system/checkconf/bad-glue-cache-bogus.conf create mode 100644 bin/tests/system/checkconf/bad-hint.conf create mode 100644 bin/tests/system/checkconf/bad-in-view-dup.conf create mode 100644 bin/tests/system/checkconf/bad-inline-options.conf create mode 100644 bin/tests/system/checkconf/bad-inline-secondary.conf create mode 100644 bin/tests/system/checkconf/bad-inline-view.conf create mode 100644 bin/tests/system/checkconf/bad-interface-interval.conf create mode 100644 bin/tests/system/checkconf/bad-ipv4-prefix-dotted1.conf create mode 100644 bin/tests/system/checkconf/bad-ipv4-prefix-dotted2.conf create mode 100644 bin/tests/system/checkconf/bad-ipv4-prefix2.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-define-default.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-define-insecure.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-define-none.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-duplicate.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-key1.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-key2.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-key3.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-key4.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir1.conf.in create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir2.conf.in create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir3.conf.in create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir4.conf.in create mode 100644 bin/tests/system/checkconf/bad-kasp-keydir5.conf.in create mode 100644 bin/tests/system/checkconf/bad-kasp-nsec3-alg.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-policy-undefined-inherited-view.conf create mode 100644 bin/tests/system/checkconf/bad-kasp-policy-undefined-inherited.conf create mode 100644 bin/tests/system/checkconf/bad-kasp10.conf create mode 100644 bin/tests/system/checkconf/bad-kasp11.conf create mode 100644 bin/tests/system/checkconf/bad-kasp12.conf create mode 100644 bin/tests/system/checkconf/bad-kasp13.conf create mode 100644 bin/tests/system/checkconf/bad-kasp2.conf create mode 100644 bin/tests/system/checkconf/bad-kasp3.conf create mode 100644 bin/tests/system/checkconf/bad-kasp4.conf create mode 100644 bin/tests/system/checkconf/bad-kasp6.conf create mode 100644 bin/tests/system/checkconf/bad-kasp7.conf create mode 100644 bin/tests/system/checkconf/bad-kasp8.conf create mode 100644 bin/tests/system/checkconf/bad-kasp9.conf create mode 100644 bin/tests/system/checkconf/bad-keep-response-order.conf create mode 100644 bin/tests/system/checkconf/bad-ksk-without-zsk.conf create mode 100644 bin/tests/system/checkconf/bad-lifetime.conf create mode 100644 bin/tests/system/checkconf/bad-lmdb-mapsize-bogus.conf create mode 100644 bin/tests/system/checkconf/bad-lmdb-mapsize-toolarge.conf create mode 100644 bin/tests/system/checkconf/bad-lmdb-mapsize-toosmall.conf create mode 100644 bin/tests/system/checkconf/bad-lmdb-mapsize-unlimited.conf create mode 100644 bin/tests/system/checkconf/bad-master-request-ixfr.conf create mode 100644 bin/tests/system/checkconf/bad-masterfile-format-map.conf create mode 100644 bin/tests/system/checkconf/bad-maxcachettl.conf create mode 100644 bin/tests/system/checkconf/bad-maxncachettl-1.conf create mode 100644 bin/tests/system/checkconf/bad-maxncachettl-2.conf create mode 100644 bin/tests/system/checkconf/bad-maxncachettl-3.conf create mode 100644 bin/tests/system/checkconf/bad-maxncachettl-4.conf create mode 100644 bin/tests/system/checkconf/bad-maxratio1.conf create mode 100644 bin/tests/system/checkconf/bad-maxratio2.conf create mode 100644 bin/tests/system/checkconf/bad-mincachettl.conf create mode 100644 bin/tests/system/checkconf/bad-minncachettl.conf create mode 100644 bin/tests/system/checkconf/bad-mirror-allow-recursion-none.conf create mode 100644 bin/tests/system/checkconf/bad-mirror-explicit-notify-yes.conf create mode 100644 bin/tests/system/checkconf/bad-mirror-non-root-zone-without-masters.conf create mode 100644 bin/tests/system/checkconf/bad-mirror-recursion-no.conf create mode 100644 bin/tests/system/checkconf/bad-mirror-zonename.conf create mode 100644 bin/tests/system/checkconf/bad-noddns.conf create mode 100644 bin/tests/system/checkconf/bad-notify-source-v6.conf create mode 100644 bin/tests/system/checkconf/bad-notify-source.conf create mode 100644 bin/tests/system/checkconf/bad-options-also-notify.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-options.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-view.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-view2.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-def-zone.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-dup.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-dupdef.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-empty.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-empty2.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-mirror.conf create mode 100644 bin/tests/system/checkconf/bad-parental-agents-notfound.conf create mode 100644 bin/tests/system/checkconf/bad-parental-source-v6.conf create mode 100644 bin/tests/system/checkconf/bad-parental-source.conf create mode 100644 bin/tests/system/checkconf/bad-port.conf create mode 100644 bin/tests/system/checkconf/bad-primaries-dup.conf create mode 100644 bin/tests/system/checkconf/bad-primaries-key.conf create mode 100644 bin/tests/system/checkconf/bad-primaries-notfound.conf create mode 100644 bin/tests/system/checkconf/bad-primaries-tls.conf create mode 100644 bin/tests/system/checkconf/bad-printtime.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-acl.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-all-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-errors-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-ipv4-prefix-length.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-ipv6-prefix-length.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-max-table-size.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-nodata-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-nxdomains-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-qps-scale.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-referrals-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-responses-per-second.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-slip.conf create mode 100644 bin/tests/system/checkconf/bad-rate-limit-window.conf create mode 100644 bin/tests/system/checkconf/bad-root-mixed-key.conf create mode 100644 bin/tests/system/checkconf/bad-rpz-too-many-zones.conf create mode 100644 bin/tests/system/checkconf/bad-rpz-ttl.conf create mode 100644 bin/tests/system/checkconf/bad-rpz-update.conf create mode 100644 bin/tests/system/checkconf/bad-rpz-zone.conf create mode 100644 bin/tests/system/checkconf/bad-sharedwritable1.conf create mode 100644 bin/tests/system/checkconf/bad-sharedwritable2.conf create mode 100644 bin/tests/system/checkconf/bad-sharedzone1.conf create mode 100644 bin/tests/system/checkconf/bad-sharedzone2.conf create mode 100644 bin/tests/system/checkconf/bad-sharedzone3.conf create mode 100644 bin/tests/system/checkconf/bad-sig-validity.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-1.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-2.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-3.conf create mode 100644 bin/tests/system/checkconf/bad-static-initial-4.conf create mode 100644 bin/tests/system/checkconf/bad-stub-masters-dialup.conf create mode 100644 bin/tests/system/checkconf/bad-transfer-source-v6.conf create mode 100644 bin/tests/system/checkconf/bad-transfer-source.conf create mode 100644 bin/tests/system/checkconf/bad-tsig.conf create mode 100644 bin/tests/system/checkconf/bad-unpaired-keys.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy1.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy10.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy11.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy12.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy13.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy14.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy15.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy16.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy17.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy18.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy19.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy2.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy20.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy3.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy4.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy5.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy6.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy7.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy8.conf create mode 100644 bin/tests/system/checkconf/bad-update-policy9.conf create mode 100644 bin/tests/system/checkconf/bad-validation-auto-key.conf create mode 100644 bin/tests/system/checkconf/bad-view-also-notify.conf create mode 100644 bin/tests/system/checkconf/bad-zsk-without-ksk.conf create mode 100644 bin/tests/system/checkconf/check-dup-records-fail.conf create mode 100644 bin/tests/system/checkconf/check-dup-records.db create mode 100644 bin/tests/system/checkconf/check-missing-zone.conf create mode 100644 bin/tests/system/checkconf/check-mixed-keys.conf create mode 100644 bin/tests/system/checkconf/check-mx-cname-fail.conf create mode 100644 bin/tests/system/checkconf/check-mx-cname.db create mode 100644 bin/tests/system/checkconf/check-mx-fail.conf create mode 100644 bin/tests/system/checkconf/check-mx.db create mode 100644 bin/tests/system/checkconf/check-names-fail.conf create mode 100644 bin/tests/system/checkconf/check-names.db create mode 100644 bin/tests/system/checkconf/check-root-ksk-2010.conf create mode 100644 bin/tests/system/checkconf/check-root-ksk-2017.conf create mode 100644 bin/tests/system/checkconf/check-root-ksk-both.conf create mode 100644 bin/tests/system/checkconf/check-root-static-ds.conf create mode 100644 bin/tests/system/checkconf/check-root-static-key.conf create mode 100644 bin/tests/system/checkconf/check-root-trusted-key.conf create mode 100644 bin/tests/system/checkconf/check-srv-cname-fail.conf create mode 100644 bin/tests/system/checkconf/check-srv-cname.db create mode 100644 bin/tests/system/checkconf/check-wildcard-no.conf create mode 100644 bin/tests/system/checkconf/check-wildcard.conf create mode 100644 bin/tests/system/checkconf/check-wildcard.db create mode 100644 bin/tests/system/checkconf/clean.sh create mode 100644 bin/tests/system/checkconf/deprecated.conf create mode 100644 bin/tests/system/checkconf/dlz-bad.conf create mode 100644 bin/tests/system/checkconf/dnssec.1 create mode 100644 bin/tests/system/checkconf/dnssec.2 create mode 100644 bin/tests/system/checkconf/dnssec.3 create mode 100644 bin/tests/system/checkconf/good-acl.conf create mode 100644 bin/tests/system/checkconf/good-allow-update-forwarding-view.conf create mode 100644 bin/tests/system/checkconf/good-allow-update-forwarding.conf create mode 100644 bin/tests/system/checkconf/good-allow-update-view.conf create mode 100644 bin/tests/system/checkconf/good-allow-update.conf create mode 100644 bin/tests/system/checkconf/good-class.conf create mode 100644 bin/tests/system/checkconf/good-dnskey-validity-3660.conf create mode 100644 bin/tests/system/checkconf/good-dnskey-validity-zero.conf create mode 100644 bin/tests/system/checkconf/good-doh-1.conf create mode 100644 bin/tests/system/checkconf/good-doh-2.conf create mode 100644 bin/tests/system/checkconf/good-doh-3.conf create mode 100644 bin/tests/system/checkconf/good-doh-4.conf create mode 100644 bin/tests/system/checkconf/good-doh-tlsopts.conf create mode 100644 bin/tests/system/checkconf/good-dot-1.conf create mode 100644 bin/tests/system/checkconf/good-dot-allow-transfer-encrypted.conf create mode 100644 bin/tests/system/checkconf/good-dot-doh-tls-nokeycert.conf create mode 100644 bin/tests/system/checkconf/good-dot-primaries-ephemeral.conf create mode 100644 bin/tests/system/checkconf/good-dot-primaries.conf create mode 100644 bin/tests/system/checkconf/good-dot-tlsopts.conf create mode 100644 bin/tests/system/checkconf/good-ds-key-1.conf create mode 100644 bin/tests/system/checkconf/good-ds-key-2.conf create mode 100644 bin/tests/system/checkconf/good-dup-managed-key.conf create mode 100644 bin/tests/system/checkconf/good-dup-trusted-key.conf create mode 100644 bin/tests/system/checkconf/good-glue-cache.conf create mode 100644 bin/tests/system/checkconf/good-initial-ds.conf create mode 100644 bin/tests/system/checkconf/good-interface-interval.conf create mode 100644 bin/tests/system/checkconf/good-kasp.conf create mode 100644 bin/tests/system/checkconf/good-key-directory.conf create mode 100644 bin/tests/system/checkconf/good-masterfile-format-raw.conf create mode 100644 bin/tests/system/checkconf/good-masterfile-format-text.conf create mode 100644 bin/tests/system/checkconf/good-masters-and-primaries.conf create mode 100644 bin/tests/system/checkconf/good-maxcachettl.conf create mode 100644 bin/tests/system/checkconf/good-maxncachettl.conf create mode 100644 bin/tests/system/checkconf/good-maxratio1.conf create mode 100644 bin/tests/system/checkconf/good-maxratio2.conf create mode 100644 bin/tests/system/checkconf/good-mincachettl.conf create mode 100644 bin/tests/system/checkconf/good-minncachettl.conf create mode 100644 bin/tests/system/checkconf/good-mirror-inherited-notify-yes.conf create mode 100644 bin/tests/system/checkconf/good-mirror-root-zone-without-masters.conf create mode 100644 bin/tests/system/checkconf/good-nested.conf create mode 100644 bin/tests/system/checkconf/good-notify-source-v6.conf create mode 100644 bin/tests/system/checkconf/good-notify-source.conf create mode 100644 bin/tests/system/checkconf/good-options-also-notify.conf create mode 100644 bin/tests/system/checkconf/good-parental-source-v6.conf create mode 100644 bin/tests/system/checkconf/good-parental-source.conf create mode 100644 bin/tests/system/checkconf/good-printtime.conf create mode 100644 bin/tests/system/checkconf/good-response-dot.conf create mode 100644 bin/tests/system/checkconf/good-rpz-ttl.conf create mode 100644 bin/tests/system/checkconf/good-rpz-update.conf create mode 100644 bin/tests/system/checkconf/good-rrset-order-none.conf create mode 100644 bin/tests/system/checkconf/good-server-christmas-tree.conf.in create mode 100644 bin/tests/system/checkconf/good-sig-signing-type.conf create mode 100644 bin/tests/system/checkconf/good-static-ds.conf create mode 100644 bin/tests/system/checkconf/good-transfer-source-v6.conf create mode 100644 bin/tests/system/checkconf/good-transfer-source.conf create mode 100644 bin/tests/system/checkconf/good-update-policy1.conf create mode 100644 bin/tests/system/checkconf/good-update-policy10.conf create mode 100644 bin/tests/system/checkconf/good-update-policy11.conf create mode 100644 bin/tests/system/checkconf/good-update-policy12.conf create mode 100644 bin/tests/system/checkconf/good-update-policy13.conf create mode 100644 bin/tests/system/checkconf/good-update-policy2.conf create mode 100644 bin/tests/system/checkconf/good-update-policy3.conf create mode 100644 bin/tests/system/checkconf/good-update-policy4.conf create mode 100644 bin/tests/system/checkconf/good-update-policy5.conf create mode 100644 bin/tests/system/checkconf/good-update-policy6.conf create mode 100644 bin/tests/system/checkconf/good-update-policy7.conf create mode 100644 bin/tests/system/checkconf/good-update-policy8.conf create mode 100644 bin/tests/system/checkconf/good-update-policy9.conf create mode 100644 bin/tests/system/checkconf/good-view-also-notify.conf create mode 100644 bin/tests/system/checkconf/good.conf create mode 100644 bin/tests/system/checkconf/good.zonelist create mode 100644 bin/tests/system/checkconf/hint-nofile.conf create mode 100644 bin/tests/system/checkconf/in-view-good.conf create mode 100644 bin/tests/system/checkconf/inline-bad.conf create mode 100644 bin/tests/system/checkconf/inline-good.conf create mode 100644 bin/tests/system/checkconf/inline-no.conf create mode 100644 bin/tests/system/checkconf/kasp-and-other-dnssec-options.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-keylen.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-lifetime.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-nsec3-alg.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-nsec3-iter.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-nsec3-salt.conf create mode 100644 bin/tests/system/checkconf/kasp-bad-signatures-refresh.conf create mode 100644 bin/tests/system/checkconf/kasp-ignore-keylen.conf create mode 100644 bin/tests/system/checkconf/kasp-warning.conf create mode 100644 bin/tests/system/checkconf/lmdb-mapsize-largest.conf create mode 100644 bin/tests/system/checkconf/lmdb-mapsize-smallest.conf create mode 100644 bin/tests/system/checkconf/max-cache-size-good.conf create mode 100644 bin/tests/system/checkconf/max-ttl.conf create mode 100644 bin/tests/system/checkconf/maxttl-bad.conf create mode 100644 bin/tests/system/checkconf/maxttl-bad.db create mode 100644 bin/tests/system/checkconf/maxttl.db create mode 100644 bin/tests/system/checkconf/notify.conf create mode 100644 bin/tests/system/checkconf/portrange-good.conf create mode 100644 bin/tests/system/checkconf/range.conf create mode 100644 bin/tests/system/checkconf/servestale.stale-refresh-time.0.conf create mode 100644 bin/tests/system/checkconf/servestale.stale-refresh-time.29.conf create mode 100644 bin/tests/system/checkconf/setup.sh create mode 100644 bin/tests/system/checkconf/shared.example.db create mode 100644 bin/tests/system/checkconf/tests.sh create mode 100644 bin/tests/system/checkconf/tests_sh_checkconf.py create mode 100644 bin/tests/system/checkconf/view-class-any1.conf create mode 100644 bin/tests/system/checkconf/view-class-any2.conf create mode 100644 bin/tests/system/checkconf/view-class-in1.conf create mode 100644 bin/tests/system/checkconf/view-class-in2.conf create mode 100644 bin/tests/system/checkconf/warn-kasp-max-zone-ttl.conf create mode 100644 bin/tests/system/checkconf/warn-keydir.conf create mode 100644 bin/tests/system/checkconf/warn-maxratio1.conf create mode 100644 bin/tests/system/checkconf/warn-notify-source.conf create mode 100644 bin/tests/system/checkconf/warn-parental-source.conf create mode 100644 bin/tests/system/checkconf/warn-random-device.conf create mode 100644 bin/tests/system/checkconf/warn-transfer-source.conf create mode 100644 bin/tests/system/checkds/README create mode 100644 bin/tests/system/checkds/clean.sh create mode 100644 bin/tests/system/checkds/ns1/named.conf.in create mode 100644 bin/tests/system/checkds/ns1/root.db.in create mode 100644 bin/tests/system/checkds/ns1/setup.sh create mode 100644 bin/tests/system/checkds/ns10/named.conf.in create mode 100644 bin/tests/system/checkds/ns10/root.db.in create mode 100644 bin/tests/system/checkds/ns2/named.conf.in create mode 100644 bin/tests/system/checkds/ns2/setup.sh create mode 100644 bin/tests/system/checkds/ns2/template.db.in create mode 100644 bin/tests/system/checkds/ns3/named.conf.in create mode 100644 bin/tests/system/checkds/ns4/named.conf.in create mode 100644 bin/tests/system/checkds/ns5/named.conf.in create mode 100644 bin/tests/system/checkds/ns5/setup.sh create mode 100644 bin/tests/system/checkds/ns5/template.db.in create mode 100644 bin/tests/system/checkds/ns6/named.conf.in create mode 100644 bin/tests/system/checkds/ns7/named.conf.in create mode 100644 bin/tests/system/checkds/ns8/named.conf.in create mode 100644 bin/tests/system/checkds/ns8/root.hint create mode 100644 bin/tests/system/checkds/ns9/named.conf.in create mode 100644 bin/tests/system/checkds/ns9/setup.sh create mode 100644 bin/tests/system/checkds/ns9/template.db.in create mode 100644 bin/tests/system/checkds/setup.sh create mode 100755 bin/tests/system/checkds/tests_checkds.py create mode 100644 bin/tests/system/checknames/clean.sh create mode 100644 bin/tests/system/checknames/ns1/fail.example.db.in create mode 100644 bin/tests/system/checknames/ns1/fail.update.db.in create mode 100644 bin/tests/system/checknames/ns1/ignore.example.db.in create mode 100644 bin/tests/system/checknames/ns1/ignore.update.db.in create mode 100644 bin/tests/system/checknames/ns1/named.conf.in create mode 100644 bin/tests/system/checknames/ns1/root.db create mode 100644 bin/tests/system/checknames/ns1/warn.example.db.in create mode 100644 bin/tests/system/checknames/ns1/warn.update.db.in create mode 100644 bin/tests/system/checknames/ns2/named.conf.in create mode 100644 bin/tests/system/checknames/ns2/root.hints create mode 100644 bin/tests/system/checknames/ns3/named.conf.in create mode 100644 bin/tests/system/checknames/ns3/root.hints create mode 100644 bin/tests/system/checknames/ns4/named.conf.in create mode 100644 bin/tests/system/checknames/ns4/primary-ignore.update.db.in create mode 100644 bin/tests/system/checknames/ns4/root.hints create mode 100644 bin/tests/system/checknames/ns5/master-ignore.update.db.in create mode 100644 bin/tests/system/checknames/ns5/named.conf.in create mode 100644 bin/tests/system/checknames/ns5/root.hints create mode 100644 bin/tests/system/checknames/setup.sh create mode 100644 bin/tests/system/checknames/tests.sh create mode 100644 bin/tests/system/checknames/tests_sh_checknames.py create mode 100644 bin/tests/system/checkzone/clean.sh create mode 100644 bin/tests/system/checkzone/setup.sh create mode 100644 bin/tests/system/checkzone/tests.sh create mode 100644 bin/tests/system/checkzone/tests_sh_checkzone.py create mode 100644 bin/tests/system/checkzone/zones/.gitattributes create mode 100644 bin/tests/system/checkzone/zones/bad-badclass.raw create mode 100644 bin/tests/system/checkzone/zones/bad-caa-rr.db create mode 100644 bin/tests/system/checkzone/zones/bad-cdnskey.db create mode 100644 bin/tests/system/checkzone/zones/bad-cds.db create mode 100644 bin/tests/system/checkzone/zones/bad-dhcid.db create mode 100644 bin/tests/system/checkzone/zones/bad-dns-sd-reverse.db create mode 100644 bin/tests/system/checkzone/zones/bad-ds.db create mode 100644 bin/tests/system/checkzone/zones/bad-eid.db create mode 100644 bin/tests/system/checkzone/zones/bad-generate-garbage.db create mode 100644 bin/tests/system/checkzone/zones/bad-generate-missing-brace.db create mode 100644 bin/tests/system/checkzone/zones/bad-generate-range.db create mode 100644 bin/tests/system/checkzone/zones/bad-generate-tkey.db create mode 100644 bin/tests/system/checkzone/zones/bad-nimloc.db create mode 100644 bin/tests/system/checkzone/zones/bad-nsap-empty.db create mode 100644 bin/tests/system/checkzone/zones/bad-nsap-odd-nibble.db create mode 100644 bin/tests/system/checkzone/zones/bad-nsec3-padded.db create mode 100644 bin/tests/system/checkzone/zones/bad-nsec3owner-padded.db create mode 100644 bin/tests/system/checkzone/zones/bad-svcb-mandatory.db create mode 100644 bin/tests/system/checkzone/zones/bad-svcb-servername.db create mode 100644 bin/tests/system/checkzone/zones/bad-svcb.db create mode 100644 bin/tests/system/checkzone/zones/bad-tkey.db create mode 100644 bin/tests/system/checkzone/zones/bad-tsig.db.in create mode 100644 bin/tests/system/checkzone/zones/bad-unspec.db create mode 100644 bin/tests/system/checkzone/zones/bad1.db create mode 100644 bin/tests/system/checkzone/zones/bad2.db create mode 100644 bin/tests/system/checkzone/zones/bad3.db create mode 100644 bin/tests/system/checkzone/zones/bad4.db create mode 100644 bin/tests/system/checkzone/zones/badttl.db create mode 100644 bin/tests/system/checkzone/zones/crashzone.db create mode 100644 bin/tests/system/checkzone/zones/delegating-ns-address-below-dname.db create mode 100644 bin/tests/system/checkzone/zones/generate-overflow.db create mode 100644 bin/tests/system/checkzone/zones/good-cdnskey.db create mode 100644 bin/tests/system/checkzone/zones/good-cds-unsigned.db create mode 100644 bin/tests/system/checkzone/zones/good-cds.db create mode 100644 bin/tests/system/checkzone/zones/good-dns-sd-reverse.db create mode 100644 bin/tests/system/checkzone/zones/good-gc-msdcs.db create mode 100644 bin/tests/system/checkzone/zones/good-generate-modifier.db create mode 100644 bin/tests/system/checkzone/zones/good-nsap.db create mode 100644 bin/tests/system/checkzone/zones/good-nsec3-nopadhash.db create mode 100644 bin/tests/system/checkzone/zones/good-occulted-ns-by-dname.db create mode 100644 bin/tests/system/checkzone/zones/good-occulted-ns-by-ns.db create mode 100644 bin/tests/system/checkzone/zones/good-spf-exception.db create mode 100644 bin/tests/system/checkzone/zones/good-svcb.db create mode 100644 bin/tests/system/checkzone/zones/good1.db create mode 100644 bin/tests/system/checkzone/zones/inherit.db create mode 100644 bin/tests/system/checkzone/zones/nowarn.inherited.owner.db create mode 100644 bin/tests/system/checkzone/zones/ns-address-below-dname.db create mode 100644 bin/tests/system/checkzone/zones/spf.db create mode 100644 bin/tests/system/checkzone/zones/test1.db create mode 100644 bin/tests/system/checkzone/zones/test2.db create mode 100644 bin/tests/system/checkzone/zones/warn.inherit.origin.db create mode 100644 bin/tests/system/checkzone/zones/warn.inherited.owner.db create mode 100644 bin/tests/system/checkzone/zones/zone1.db create mode 100644 bin/tests/system/ckdnsrps.sh create mode 100644 bin/tests/system/cleanall.sh create mode 100644 bin/tests/system/common/controls.conf.in create mode 100644 bin/tests/system/common/rndc.conf create mode 100644 bin/tests/system/common/rndc.key create mode 100644 bin/tests/system/common/root.hint create mode 100644 bin/tests/system/conf.sh.common create mode 100644 bin/tests/system/conf.sh.in create mode 100644 bin/tests/system/conftest.py create mode 100644 bin/tests/system/cookie/ans9/ans.py create mode 100644 bin/tests/system/cookie/bad-cookie-badaes.conf create mode 100644 bin/tests/system/cookie/bad-cookie-badhex.conf create mode 100644 bin/tests/system/cookie/bad-cookie-badsiphash24.conf create mode 100644 bin/tests/system/cookie/bad-cookie-toolong.conf create mode 100644 bin/tests/system/cookie/clean.sh create mode 100644 bin/tests/system/cookie/good-cookie-aes.conf create mode 100644 bin/tests/system/cookie/good-cookie-siphash24.conf create mode 100644 bin/tests/system/cookie/ns1/example.db create mode 100644 bin/tests/system/cookie/ns1/named.conf.in create mode 100644 bin/tests/system/cookie/ns1/root.hint create mode 100644 bin/tests/system/cookie/ns2/named.conf.in create mode 100644 bin/tests/system/cookie/ns2/root.db create mode 100644 bin/tests/system/cookie/ns3/named.conf.in create mode 100644 bin/tests/system/cookie/ns3/root.hint create mode 100644 bin/tests/system/cookie/ns4/named.conf.in create mode 100644 bin/tests/system/cookie/ns4/root.hint create mode 100644 bin/tests/system/cookie/ns5/named.conf.in create mode 100644 bin/tests/system/cookie/ns5/root.hint create mode 100644 bin/tests/system/cookie/ns6/named.conf.in create mode 100644 bin/tests/system/cookie/ns6/root.hint create mode 100644 bin/tests/system/cookie/ns7/named.conf.in create mode 100644 bin/tests/system/cookie/ns7/root.db create mode 100644 bin/tests/system/cookie/ns8/example.db create mode 100644 bin/tests/system/cookie/ns8/named.conf.in create mode 100644 bin/tests/system/cookie/setup.sh create mode 100755 bin/tests/system/cookie/tests.sh create mode 100644 bin/tests/system/cookie/tests_sh_cookie.py create mode 100755 bin/tests/system/custom-test-driver create mode 100644 bin/tests/system/database/clean.sh create mode 100644 bin/tests/system/database/ns1/named1.conf.in create mode 100644 bin/tests/system/database/ns1/named2.conf.in create mode 100644 bin/tests/system/database/setup.sh create mode 100644 bin/tests/system/database/tests.sh create mode 100644 bin/tests/system/database/tests_sh_database.py create mode 100644 bin/tests/system/dialup/clean.sh create mode 100644 bin/tests/system/dialup/ns1/example.db create mode 100644 bin/tests/system/dialup/ns1/named.conf.in create mode 100644 bin/tests/system/dialup/ns1/root.db create mode 100644 bin/tests/system/dialup/ns2/hint.db create mode 100644 bin/tests/system/dialup/ns2/named.conf.in create mode 100644 bin/tests/system/dialup/ns3/hint.db create mode 100644 bin/tests/system/dialup/ns3/named.conf.in create mode 100644 bin/tests/system/dialup/setup.sh create mode 100644 bin/tests/system/dialup/tests.sh create mode 100644 bin/tests/system/dialup/tests_sh_dialup.py create mode 100644 bin/tests/system/digcomp.pl create mode 100644 bin/tests/system/digdelv/ans4/startme create mode 100644 bin/tests/system/digdelv/ans5/ans.pl create mode 100755 bin/tests/system/digdelv/ans6/ans.pl create mode 100755 bin/tests/system/digdelv/ans7/ans.pl create mode 100644 bin/tests/system/digdelv/ans8/ans.py create mode 100644 bin/tests/system/digdelv/clean.sh create mode 100644 bin/tests/system/digdelv/ns1/named.conf.in create mode 100644 bin/tests/system/digdelv/ns1/root.db create mode 100644 bin/tests/system/digdelv/ns2/example.db.in create mode 100644 bin/tests/system/digdelv/ns2/named.conf.in create mode 100644 bin/tests/system/digdelv/ns2/sign.sh create mode 100644 bin/tests/system/digdelv/ns3/named.conf.in create mode 100644 bin/tests/system/digdelv/setup.sh create mode 100644 bin/tests/system/digdelv/tests.sh create mode 100644 bin/tests/system/digdelv/tests_sh_digdelv.py create mode 100644 bin/tests/system/digdelv/yamlget.py create mode 100644 bin/tests/system/dispatch/ans3/ans.py create mode 100644 bin/tests/system/dispatch/clean.sh create mode 100644 bin/tests/system/dispatch/ns1/named.conf.in create mode 100644 bin/tests/system/dispatch/ns1/root.db create mode 100644 bin/tests/system/dispatch/ns2/example.db create mode 100644 bin/tests/system/dispatch/ns2/named.conf.in create mode 100644 bin/tests/system/dispatch/setup.sh create mode 100644 bin/tests/system/dispatch/tests_connreset.py create mode 100644 bin/tests/system/ditch.pl create mode 100644 bin/tests/system/dlzexternal/clean.sh create mode 100644 bin/tests/system/dlzexternal/driver/Makefile.am create mode 100644 bin/tests/system/dlzexternal/driver/Makefile.in create mode 100644 bin/tests/system/dlzexternal/driver/driver.c create mode 100644 bin/tests/system/dlzexternal/driver/driver.h create mode 100644 bin/tests/system/dlzexternal/ns1/named.conf.in create mode 100644 bin/tests/system/dlzexternal/ns1/root.db create mode 100644 bin/tests/system/dlzexternal/prereq.sh create mode 100644 bin/tests/system/dlzexternal/setup.sh create mode 100644 bin/tests/system/dlzexternal/tests.sh create mode 100644 bin/tests/system/dlzexternal/tests_sh_dlzexternal.py create mode 100644 bin/tests/system/dns64/clean.sh create mode 100644 bin/tests/system/dns64/conf/bad1.conf create mode 100644 bin/tests/system/dns64/conf/bad10.conf create mode 100644 bin/tests/system/dns64/conf/bad11.conf create mode 100644 bin/tests/system/dns64/conf/bad12.conf create mode 100644 bin/tests/system/dns64/conf/bad13.conf create mode 100644 bin/tests/system/dns64/conf/bad14.conf create mode 100644 bin/tests/system/dns64/conf/bad15.conf create mode 100644 bin/tests/system/dns64/conf/bad16.conf create mode 100644 bin/tests/system/dns64/conf/bad17.conf create mode 100644 bin/tests/system/dns64/conf/bad18.conf create mode 100644 bin/tests/system/dns64/conf/bad19.conf create mode 100644 bin/tests/system/dns64/conf/bad2.conf create mode 100644 bin/tests/system/dns64/conf/bad3.conf create mode 100644 bin/tests/system/dns64/conf/bad4.conf create mode 100644 bin/tests/system/dns64/conf/bad5.conf create mode 100644 bin/tests/system/dns64/conf/bad6.conf create mode 100644 bin/tests/system/dns64/conf/bad7.conf create mode 100644 bin/tests/system/dns64/conf/bad8.conf create mode 100644 bin/tests/system/dns64/conf/bad9.conf create mode 100644 bin/tests/system/dns64/conf/good1.conf create mode 100644 bin/tests/system/dns64/conf/good2.conf create mode 100644 bin/tests/system/dns64/conf/good3.conf create mode 100644 bin/tests/system/dns64/conf/good4.conf create mode 100644 bin/tests/system/dns64/conf/good5.conf create mode 100644 bin/tests/system/dns64/ns1/example.db create mode 100644 bin/tests/system/dns64/ns1/ipv4only.arpa.db create mode 100644 bin/tests/system/dns64/ns1/named.conf1.in create mode 100644 bin/tests/system/dns64/ns1/named.conf2.in create mode 100644 bin/tests/system/dns64/ns1/named.conf3.in create mode 100644 bin/tests/system/dns64/ns1/root.db create mode 100644 bin/tests/system/dns64/ns1/sign.sh create mode 100644 bin/tests/system/dns64/ns2/named.conf.in create mode 100644 bin/tests/system/dns64/ns2/rpz.db create mode 100644 bin/tests/system/dns64/setup.sh create mode 100644 bin/tests/system/dns64/tests.sh create mode 100644 bin/tests/system/dns64/tests_sh_dns64.py create mode 100644 bin/tests/system/dnssec/README create mode 100644 bin/tests/system/dnssec/ans10/ans.py create mode 100644 bin/tests/system/dnssec/clean.sh create mode 100644 bin/tests/system/dnssec/dnssec_update_test.pl create mode 100644 bin/tests/system/dnssec/ns1/named.conf.in create mode 100644 bin/tests/system/dnssec/ns1/root.db.in create mode 100644 bin/tests/system/dnssec/ns1/sign.sh create mode 100644 bin/tests/system/dnssec/ns2/algroll.db.in create mode 100644 bin/tests/system/dnssec/ns2/badparam.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey-auto.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey-kskonly.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey-update.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cdnskey.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds-auto.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds-kskonly.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds-update.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/cds.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/child.nsec3.example.db create mode 100644 bin/tests/system/dnssec/ns2/child.optout.example.db create mode 100644 bin/tests/system/dnssec/ns2/corp.db create mode 100644 bin/tests/system/dnssec/ns2/dst.example.db.in create mode 100644 bin/tests/system/dnssec/ns2/example.db.in create mode 100644 bin/tests/system/dnssec/ns2/hours-vs-days.db.in create mode 100644 bin/tests/system/dnssec/ns2/in-addr.arpa.db.in create mode 100644 bin/tests/system/dnssec/ns2/insecure.secure.example.db create mode 100644 bin/tests/system/dnssec/ns2/key.db.in create mode 100644 bin/tests/system/dnssec/ns2/named.conf.in create mode 100644 bin/tests/system/dnssec/ns2/private.secure.example.db.in create mode 100644 bin/tests/system/dnssec/ns2/rfc2335.example.db create mode 100644 bin/tests/system/dnssec/ns2/sign.sh create mode 100644 bin/tests/system/dnssec/ns2/single-nsec3.db.in create mode 100644 bin/tests/system/dnssec/ns2/template.secure.db.in create mode 100644 bin/tests/system/dnssec/ns2/too-many-iterations.db.in create mode 100644 bin/tests/system/dnssec/ns3/auto-nsec.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/auto-nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/bogus.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dname-at-apex-nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dnskey-nsec3-unknown.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dnskey-unknown.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dnskey-unsupported-2.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dnskey-unsupported.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/dynamic.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/expired.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/expiring.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/future.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/generic.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/inline.example.db create mode 100644 bin/tests/system/dnssec/ns3/insecure.below-cname.example.db create mode 100644 bin/tests/system/dnssec/ns3/insecure.example.db create mode 100644 bin/tests/system/dnssec/ns3/insecure.nsec3.example.db create mode 100644 bin/tests/system/dnssec/ns3/insecure.optout.example.db create mode 100644 bin/tests/system/dnssec/ns3/insecure2.example.db create mode 100644 bin/tests/system/dnssec/ns3/key.db.in create mode 100644 bin/tests/system/dnssec/ns3/kskonly.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/lower.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/managed-future.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/multiple.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/named.conf.in create mode 100644 bin/tests/system/dnssec/ns3/nosign.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/nsec3-unknown.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/nsec3.nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/nsec3.optout.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/occluded.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/optout-unknown.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/optout.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/optout.nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/optout.optout.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/publish-inactive.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/rsasha256.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/rsasha512.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/secure.below-cname.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/secure.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/secure.nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/secure.optout.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/siginterval.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/siginterval1.conf create mode 100644 bin/tests/system/dnssec/ns3/siginterval2.conf create mode 100644 bin/tests/system/dnssec/ns3/sign.sh create mode 100644 bin/tests/system/dnssec/ns3/split-dnssec.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/split-smart.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/ttlpatch.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/unsupported-algorithm.key create mode 100644 bin/tests/system/dnssec/ns3/update-nsec3.example.db.in create mode 100644 bin/tests/system/dnssec/ns3/upper.example.db.in create mode 100644 bin/tests/system/dnssec/ns4/managed-keys.bind.in create mode 100644 bin/tests/system/dnssec/ns4/named1.conf.in create mode 100644 bin/tests/system/dnssec/ns4/named2.conf.in create mode 100644 bin/tests/system/dnssec/ns4/named3.conf.in create mode 100644 bin/tests/system/dnssec/ns4/named4.conf.in create mode 100644 bin/tests/system/dnssec/ns4/named5.conf.in create mode 100644 bin/tests/system/dnssec/ns5/named1.conf.in create mode 100644 bin/tests/system/dnssec/ns5/named2.conf.in create mode 100644 bin/tests/system/dnssec/ns5/sign.sh create mode 100644 bin/tests/system/dnssec/ns6/named.args create mode 100644 bin/tests/system/dnssec/ns6/named.conf.in create mode 100644 bin/tests/system/dnssec/ns6/optout-tld.db.in create mode 100644 bin/tests/system/dnssec/ns6/sign.sh create mode 100644 bin/tests/system/dnssec/ns7/named.conf.in create mode 100644 bin/tests/system/dnssec/ns7/named.nosoa create mode 100644 bin/tests/system/dnssec/ns7/nosoa.secure.example.db create mode 100644 bin/tests/system/dnssec/ns7/sign.sh create mode 100644 bin/tests/system/dnssec/ns7/split-rrsig.db.in create mode 100644 bin/tests/system/dnssec/ns8/named.conf.in create mode 100644 bin/tests/system/dnssec/ns9/named.conf.in create mode 100755 bin/tests/system/dnssec/ntadiff.pl create mode 100644 bin/tests/system/dnssec/setup.sh create mode 100644 bin/tests/system/dnssec/signer/example.db.in create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+008+15002.key create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+008+15002.private create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+008+63613.key create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+008+63613.private create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+010+18240.key create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+010+18240.private create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+010+28633.key create mode 100644 bin/tests/system/dnssec/signer/general/Kexample.com.+010+28633.private create mode 100644 bin/tests/system/dnssec/signer/general/bogus-ksk.key create mode 100644 bin/tests/system/dnssec/signer/general/bogus-zsk.key create mode 100644 bin/tests/system/dnssec/signer/general/test1.zone create mode 100644 bin/tests/system/dnssec/signer/general/test2.zone create mode 100644 bin/tests/system/dnssec/signer/general/test3.zone create mode 100644 bin/tests/system/dnssec/signer/general/test4.zone create mode 100644 bin/tests/system/dnssec/signer/general/test5.zone create mode 100644 bin/tests/system/dnssec/signer/general/test6.zone create mode 100644 bin/tests/system/dnssec/signer/general/test7.zone create mode 100644 bin/tests/system/dnssec/signer/general/test8.zone create mode 100644 bin/tests/system/dnssec/signer/general/test9.zone create mode 100644 bin/tests/system/dnssec/signer/prepub.db.in create mode 100644 bin/tests/system/dnssec/signer/remove.db.in create mode 100644 bin/tests/system/dnssec/signer/remove2.db.in create mode 100644 bin/tests/system/dnssec/tests.sh create mode 100644 bin/tests/system/dnssec/tests_sh_dnssec.py create mode 100644 bin/tests/system/dnstap/README create mode 100644 bin/tests/system/dnstap/bad-fstrm-reopen-interval.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-buffer-hint-max.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-buffer-hint-min.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-flush-timeout-max.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-flush-timeout-min.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-input-queue-size-max.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-input-queue-size-min.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-input-queue-size-po2.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-output-notify-threshold.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-output-queue-size-max.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-output-queue-size-min.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-reopen-interval-max.conf create mode 100644 bin/tests/system/dnstap/bad-fstrm-set-reopen-interval-min.conf create mode 100644 bin/tests/system/dnstap/bad-missing-dnstap-output-view.conf create mode 100644 bin/tests/system/dnstap/bad-missing-dnstap-output.conf create mode 100644 bin/tests/system/dnstap/bad-size-version.conf create mode 100644 bin/tests/system/dnstap/clean.sh create mode 100644 bin/tests/system/dnstap/good-dnstap-in-options.conf create mode 100644 bin/tests/system/dnstap/good-dnstap-in-view.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-reopen-interval.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-buffer-hint.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-flush-timeout.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-input-queue-size.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-output-notify-threshold.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-output-queue-model-mpsc.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-output-queue-model-spsc.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-output-queue-size.conf create mode 100644 bin/tests/system/dnstap/good-fstrm-set-reopen-interval.conf create mode 100644 bin/tests/system/dnstap/good-size-unlimited.conf create mode 100644 bin/tests/system/dnstap/good-size-version.conf create mode 100644 bin/tests/system/dnstap/large-answer.fstrm create mode 100644 bin/tests/system/dnstap/ns1/named.conf.in create mode 100644 bin/tests/system/dnstap/ns1/root.db create mode 100644 bin/tests/system/dnstap/ns2/example.db.in create mode 100644 bin/tests/system/dnstap/ns2/named.conf.in create mode 100644 bin/tests/system/dnstap/ns3/named.args create mode 100644 bin/tests/system/dnstap/ns3/named.conf.in create mode 100644 bin/tests/system/dnstap/ns4/named.conf.in create mode 100644 bin/tests/system/dnstap/prereq.sh create mode 100644 bin/tests/system/dnstap/setup.sh create mode 100644 bin/tests/system/dnstap/tests.sh create mode 100644 bin/tests/system/dnstap/tests_dnstap.py create mode 100644 bin/tests/system/dnstap/tests_sh_dnstap.py create mode 100644 bin/tests/system/dnstap/ydump.py create mode 100644 bin/tests/system/doth/CA/CA.cfg create mode 100644 bin/tests/system/doth/CA/CA.pem create mode 100644 bin/tests/system/doth/CA/README create mode 100644 bin/tests/system/doth/CA/certs/srv01.client01.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.client01.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv01.client02-ns2.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.client02-ns2.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv01.client03-ns2-expired.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.client03-ns2-expired.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt01.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt01.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt02-no-san.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt02-no-san.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt03-expired.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv01.crt03-expired.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv02.crt01.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv02.crt01.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv03.crt01.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv03.crt01.example.com.pem create mode 100644 bin/tests/system/doth/CA/certs/srv04.crt01.example.com.key create mode 100644 bin/tests/system/doth/CA/certs/srv04.crt01.example.com.pem create mode 100644 bin/tests/system/doth/CA/index.txt create mode 100644 bin/tests/system/doth/CA/index.txt.attr create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52001.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52002.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52003.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52004.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52005.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52006.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52007.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52008.pem create mode 100644 bin/tests/system/doth/CA/newcerts/6BB3183CDEF52009.pem create mode 100644 bin/tests/system/doth/CA/private/CA.key create mode 100644 bin/tests/system/doth/CA/serial create mode 100644 bin/tests/system/doth/README.curl create mode 100644 bin/tests/system/doth/clean.sh create mode 100644 bin/tests/system/doth/conftest.py create mode 100644 bin/tests/system/doth/dhparam3072.pem create mode 100644 bin/tests/system/doth/example.axfr.good create mode 100644 bin/tests/system/doth/example8.axfr.good create mode 100755 bin/tests/system/doth/get_openssl_version.py create mode 100644 bin/tests/system/doth/ns1/named.conf.in create mode 100644 bin/tests/system/doth/ns1/root.db create mode 100644 bin/tests/system/doth/ns2/cert.pem create mode 100644 bin/tests/system/doth/ns2/key.pem create mode 100644 bin/tests/system/doth/ns2/named.conf.in create mode 100644 bin/tests/system/doth/ns3/named.conf.in create mode 100644 bin/tests/system/doth/ns4/named.conf.in create mode 100644 bin/tests/system/doth/prereq.sh create mode 100644 bin/tests/system/doth/setup.sh create mode 100755 bin/tests/system/doth/stress_http_quota.py create mode 100644 bin/tests/system/doth/tests.sh create mode 100644 bin/tests/system/doth/tests_gnutls.py create mode 100644 bin/tests/system/doth/tests_sh_doth.py create mode 100644 bin/tests/system/doth/tests_sslyze.py create mode 100644 bin/tests/system/dsdigest/clean.sh create mode 100644 bin/tests/system/dsdigest/ns1/named.conf.in create mode 100644 bin/tests/system/dsdigest/ns1/root.db.in create mode 100644 bin/tests/system/dsdigest/ns1/sign.sh create mode 100644 bin/tests/system/dsdigest/ns2/bad.db.in create mode 100644 bin/tests/system/dsdigest/ns2/good.db.in create mode 100644 bin/tests/system/dsdigest/ns2/named.conf.in create mode 100644 bin/tests/system/dsdigest/ns2/sign.sh create mode 100644 bin/tests/system/dsdigest/ns3/named.conf.in create mode 100644 bin/tests/system/dsdigest/ns4/named.conf.in create mode 100644 bin/tests/system/dsdigest/setup.sh create mode 100644 bin/tests/system/dsdigest/tests.sh create mode 100644 bin/tests/system/dsdigest/tests_sh_dsdigest.py create mode 100644 bin/tests/system/dupsigs/check_journal.pl create mode 100644 bin/tests/system/dupsigs/clean.sh create mode 100644 bin/tests/system/dupsigs/ns1/named.args create mode 100644 bin/tests/system/dupsigs/ns1/named.conf.in create mode 100644 bin/tests/system/dupsigs/ns1/reset_keys.sh create mode 100644 bin/tests/system/dupsigs/ns1/signing.test.db.in create mode 100644 bin/tests/system/dupsigs/setup.sh create mode 100644 bin/tests/system/dupsigs/tests.sh create mode 100644 bin/tests/system/dupsigs/tests_sh_dupsigs.py create mode 100644 bin/tests/system/dyndb/clean.sh create mode 100644 bin/tests/system/dyndb/driver/AUTHORS create mode 100644 bin/tests/system/dyndb/driver/Makefile.am create mode 100644 bin/tests/system/dyndb/driver/Makefile.in create mode 100644 bin/tests/system/dyndb/driver/README create mode 100644 bin/tests/system/dyndb/driver/db.c create mode 100644 bin/tests/system/dyndb/driver/db.h create mode 100644 bin/tests/system/dyndb/driver/driver.c create mode 100644 bin/tests/system/dyndb/driver/instance.c create mode 100644 bin/tests/system/dyndb/driver/instance.h create mode 100644 bin/tests/system/dyndb/driver/lock.c create mode 100644 bin/tests/system/dyndb/driver/lock.h create mode 100644 bin/tests/system/dyndb/driver/log.c create mode 100644 bin/tests/system/dyndb/driver/log.h create mode 100644 bin/tests/system/dyndb/driver/syncptr.c create mode 100644 bin/tests/system/dyndb/driver/syncptr.h create mode 100644 bin/tests/system/dyndb/driver/util.h create mode 100644 bin/tests/system/dyndb/driver/zone.c create mode 100644 bin/tests/system/dyndb/driver/zone.h create mode 100644 bin/tests/system/dyndb/ns1/named.conf.in create mode 100644 bin/tests/system/dyndb/prereq.sh create mode 100644 bin/tests/system/dyndb/setup.sh create mode 100644 bin/tests/system/dyndb/tests.sh create mode 100644 bin/tests/system/dyndb/tests_sh_dyndb.py create mode 100644 bin/tests/system/ecdsa/clean.sh create mode 100644 bin/tests/system/ecdsa/ns1/named.conf.in create mode 100644 bin/tests/system/ecdsa/ns1/root.db.in create mode 100644 bin/tests/system/ecdsa/ns1/sign.sh create mode 100644 bin/tests/system/ecdsa/ns2/named.conf.in create mode 100644 bin/tests/system/ecdsa/ns3/named.conf.in create mode 100644 bin/tests/system/ecdsa/setup.sh create mode 100644 bin/tests/system/ecdsa/tests.sh create mode 100644 bin/tests/system/ecdsa/tests_sh_ecdsa.py create mode 100644 bin/tests/system/eddsa/clean.sh create mode 100644 bin/tests/system/eddsa/ns1/named.conf.in create mode 100644 bin/tests/system/eddsa/ns1/root.db.in create mode 100644 bin/tests/system/eddsa/ns1/sign.sh create mode 100644 bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.key create mode 100644 bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.private create mode 100644 bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.key create mode 100644 bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.private create mode 100644 bin/tests/system/eddsa/ns2/example.com.db.in create mode 100644 bin/tests/system/eddsa/ns2/named.conf.in create mode 100644 bin/tests/system/eddsa/ns2/sign.sh create mode 100644 bin/tests/system/eddsa/ns3/Xexample.com.+016+09713.key create mode 100644 bin/tests/system/eddsa/ns3/Xexample.com.+016+09713.private create mode 100644 bin/tests/system/eddsa/ns3/Xexample.com.+016+38353.key create mode 100644 bin/tests/system/eddsa/ns3/Xexample.com.+016+38353.private create mode 100644 bin/tests/system/eddsa/ns3/example.com.db.in create mode 100644 bin/tests/system/eddsa/ns3/named.conf.in create mode 100644 bin/tests/system/eddsa/ns3/sign.sh create mode 100644 bin/tests/system/eddsa/prereq.sh create mode 100644 bin/tests/system/eddsa/setup.sh create mode 100644 bin/tests/system/eddsa/tests.sh create mode 100644 bin/tests/system/eddsa/tests_sh_eddsa.py create mode 100644 bin/tests/system/ednscompliance/clean.sh create mode 100644 bin/tests/system/ednscompliance/ns1/named.conf.in create mode 100644 bin/tests/system/ednscompliance/ns1/root.db create mode 100644 bin/tests/system/ednscompliance/setup.sh create mode 100644 bin/tests/system/ednscompliance/tests.sh create mode 100644 bin/tests/system/ednscompliance/tests_sh_ednscompliance.py create mode 100644 bin/tests/system/emptyzones/clean.sh create mode 100644 bin/tests/system/emptyzones/ns1/empty.db create mode 100644 bin/tests/system/emptyzones/ns1/named1.conf.in create mode 100644 bin/tests/system/emptyzones/ns1/named2.conf.in create mode 100644 bin/tests/system/emptyzones/ns1/rfc1918.zones create mode 100644 bin/tests/system/emptyzones/ns1/root.hint create mode 100644 bin/tests/system/emptyzones/setup.sh create mode 100644 bin/tests/system/emptyzones/tests.sh create mode 100644 bin/tests/system/emptyzones/tests_sh_emptyzones.py create mode 100644 bin/tests/system/enginepkcs11/2037-pk11_numbits-crash-test.pkt create mode 100644 bin/tests/system/enginepkcs11/clean.sh create mode 100644 bin/tests/system/enginepkcs11/ns1/named.args create mode 100644 bin/tests/system/enginepkcs11/ns1/named.conf.in create mode 100644 bin/tests/system/enginepkcs11/ns1/template.db.in create mode 100644 bin/tests/system/enginepkcs11/prereq.sh create mode 100644 bin/tests/system/enginepkcs11/setup.sh create mode 100644 bin/tests/system/enginepkcs11/tests.sh create mode 100644 bin/tests/system/enginepkcs11/tests_sh_enginepkcs11.py create mode 100644 bin/tests/system/feature-test.c create mode 100644 bin/tests/system/fetchlimit/ans4/ans.pl create mode 100644 bin/tests/system/fetchlimit/clean.sh create mode 100644 bin/tests/system/fetchlimit/ns1/named.conf.in create mode 100644 bin/tests/system/fetchlimit/ns1/root.db create mode 100644 bin/tests/system/fetchlimit/ns2/example.db create mode 100644 bin/tests/system/fetchlimit/ns2/named.conf.in create mode 100644 bin/tests/system/fetchlimit/ns3/named.args create mode 100644 bin/tests/system/fetchlimit/ns3/named1.conf.in create mode 100644 bin/tests/system/fetchlimit/ns3/named2.conf.in create mode 100644 bin/tests/system/fetchlimit/ns3/named3.conf.in create mode 100644 bin/tests/system/fetchlimit/ns3/root.hint create mode 100644 bin/tests/system/fetchlimit/ns5/named1.conf.in create mode 100644 bin/tests/system/fetchlimit/ns5/named2.conf.in create mode 100644 bin/tests/system/fetchlimit/ns5/root.hint create mode 100644 bin/tests/system/fetchlimit/setup.sh create mode 100644 bin/tests/system/fetchlimit/tests.sh create mode 100644 bin/tests/system/fetchlimit/tests_sh_fetchlimit.py create mode 100644 bin/tests/system/filter-aaaa/clean.sh create mode 100644 bin/tests/system/filter-aaaa/conf/bad1.conf create mode 100644 bin/tests/system/filter-aaaa/conf/bad2.conf create mode 100644 bin/tests/system/filter-aaaa/conf/bad3.conf create mode 100644 bin/tests/system/filter-aaaa/conf/bad4.conf create mode 100644 bin/tests/system/filter-aaaa/conf/bad5.conf create mode 100644 bin/tests/system/filter-aaaa/conf/good1.conf create mode 100644 bin/tests/system/filter-aaaa/conf/good2.conf create mode 100644 bin/tests/system/filter-aaaa/conf/good3.conf create mode 100644 bin/tests/system/filter-aaaa/conf/good4.conf create mode 100644 bin/tests/system/filter-aaaa/conf/good5.conf create mode 100644 bin/tests/system/filter-aaaa/ns1/named1.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns1/named2.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns1/root.db create mode 100755 bin/tests/system/filter-aaaa/ns1/sign.sh create mode 100644 bin/tests/system/filter-aaaa/ns1/signed.db.in create mode 100644 bin/tests/system/filter-aaaa/ns1/unsigned.db create mode 100644 bin/tests/system/filter-aaaa/ns2/hints create mode 100644 bin/tests/system/filter-aaaa/ns2/named1.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns2/named2.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns3/hints create mode 100644 bin/tests/system/filter-aaaa/ns3/named1.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns3/named2.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns4/named1.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns4/named2.conf.in create mode 100644 bin/tests/system/filter-aaaa/ns4/root.db create mode 100755 bin/tests/system/filter-aaaa/ns4/sign.sh create mode 100644 bin/tests/system/filter-aaaa/ns4/signed.db.in create mode 100644 bin/tests/system/filter-aaaa/ns4/unsigned.db create mode 100644 bin/tests/system/filter-aaaa/ns5/hints create mode 100644 bin/tests/system/filter-aaaa/ns5/named.conf.in create mode 100644 bin/tests/system/filter-aaaa/setup.sh create mode 100644 bin/tests/system/filter-aaaa/tests.sh create mode 100644 bin/tests/system/filter-aaaa/tests_sh_filter_aaaa.py create mode 100644 bin/tests/system/formerr/clean.sh create mode 100644 bin/tests/system/formerr/formerr.pl create mode 100644 bin/tests/system/formerr/nametoolong create mode 100644 bin/tests/system/formerr/noquestions create mode 100644 bin/tests/system/formerr/ns1/named.conf.in create mode 100644 bin/tests/system/formerr/ns1/root.db create mode 100644 bin/tests/system/formerr/setup.sh create mode 100644 bin/tests/system/formerr/tests.sh create mode 100644 bin/tests/system/formerr/tests_sh_formerr.py create mode 100644 bin/tests/system/formerr/twoquestions create mode 100644 bin/tests/system/forward/ans11/ans.py create mode 100644 bin/tests/system/forward/ans6/ans.pl create mode 100644 bin/tests/system/forward/clean.sh create mode 100644 bin/tests/system/forward/ns1/diditwork.net.db create mode 100644 bin/tests/system/forward/ns1/example.db create mode 100644 bin/tests/system/forward/ns1/named.conf.in create mode 100644 bin/tests/system/forward/ns1/net.example.lll create mode 100644 bin/tests/system/forward/ns1/root.db.in create mode 100644 bin/tests/system/forward/ns1/sign.sh create mode 100644 bin/tests/system/forward/ns1/sld.tld.db create mode 100644 bin/tests/system/forward/ns1/spoofed.net.db create mode 100644 bin/tests/system/forward/ns1/sub.local.net.db create mode 100644 bin/tests/system/forward/ns10/fakenet.zone create mode 100644 bin/tests/system/forward/ns10/fakenet2.zone create mode 100644 bin/tests/system/forward/ns10/fakesublocalnet.zone create mode 100644 bin/tests/system/forward/ns10/fakesublocaltld.zone create mode 100644 bin/tests/system/forward/ns10/named.conf.in create mode 100644 bin/tests/system/forward/ns10/net.example.lll create mode 100644 bin/tests/system/forward/ns10/spoofednet.zone create mode 100644 bin/tests/system/forward/ns2/example.db create mode 100644 bin/tests/system/forward/ns2/named.conf.in create mode 100644 bin/tests/system/forward/ns2/root.db create mode 100644 bin/tests/system/forward/ns2/tld.db create mode 100644 bin/tests/system/forward/ns3/named1.conf.in create mode 100644 bin/tests/system/forward/ns3/named2.conf.in create mode 100644 bin/tests/system/forward/ns3/root.db create mode 100644 bin/tests/system/forward/ns3/root2.db create mode 100644 bin/tests/system/forward/ns4/malicious.db create mode 100644 bin/tests/system/forward/ns4/named.conf.in create mode 100644 bin/tests/system/forward/ns4/root.db create mode 100644 bin/tests/system/forward/ns4/sibling.tld.db create mode 100644 bin/tests/system/forward/ns5/named.conf.in create mode 100644 bin/tests/system/forward/ns5/rebind.db create mode 100644 bin/tests/system/forward/ns5/root.db create mode 100644 bin/tests/system/forward/ns7/named.conf.in create mode 100644 bin/tests/system/forward/ns7/root.db create mode 100644 bin/tests/system/forward/ns8/named.conf.in create mode 100644 bin/tests/system/forward/ns8/root.db create mode 100644 bin/tests/system/forward/ns8/sub.local.tld.db create mode 100644 bin/tests/system/forward/ns9/local.net.db create mode 100644 bin/tests/system/forward/ns9/local.tld.db create mode 100644 bin/tests/system/forward/ns9/named1.conf.in create mode 100644 bin/tests/system/forward/ns9/named2.conf.in create mode 100644 bin/tests/system/forward/ns9/named3.conf.in create mode 100644 bin/tests/system/forward/ns9/named4.conf.in create mode 100644 bin/tests/system/forward/ns9/root.db create mode 100644 bin/tests/system/forward/rfc1918-inherited.conf create mode 100644 bin/tests/system/forward/rfc1918-notinherited.conf create mode 100644 bin/tests/system/forward/setup.sh create mode 100644 bin/tests/system/forward/tests.sh create mode 100644 bin/tests/system/forward/tests_sh_forward.py create mode 100644 bin/tests/system/forward/ula-inherited.conf create mode 100644 bin/tests/system/forward/ula-notinherited.conf create mode 100644 bin/tests/system/fromhex.pl create mode 100644 bin/tests/system/genzone.sh create mode 100644 bin/tests/system/geoip2/clean.sh create mode 100644 bin/tests/system/geoip2/conf/bad-areacode.conf create mode 100644 bin/tests/system/geoip2/conf/bad-dbname.conf create mode 100644 bin/tests/system/geoip2/conf/bad-netspeed.conf create mode 100644 bin/tests/system/geoip2/conf/bad-regiondb.conf create mode 100644 bin/tests/system/geoip2/conf/bad-threeletter.conf create mode 100644 bin/tests/system/geoip2/conf/good-options.conf create mode 100644 bin/tests/system/geoip2/data/GeoIP2-City.json create mode 100644 bin/tests/system/geoip2/data/GeoIP2-City.mmdb create mode 100644 bin/tests/system/geoip2/data/GeoIP2-Country.json create mode 100644 bin/tests/system/geoip2/data/GeoIP2-Country.mmdb create mode 100644 bin/tests/system/geoip2/data/GeoIP2-Domain.json create mode 100644 bin/tests/system/geoip2/data/GeoIP2-Domain.mmdb create mode 100644 bin/tests/system/geoip2/data/GeoIP2-ISP.json create mode 100644 bin/tests/system/geoip2/data/GeoIP2-ISP.mmdb create mode 100644 bin/tests/system/geoip2/data/GeoLite2-ASN.json create mode 100644 bin/tests/system/geoip2/data/GeoLite2-ASN.mmdb create mode 100644 bin/tests/system/geoip2/data/README.md create mode 100755 bin/tests/system/geoip2/data/write-test-data.pl create mode 100644 bin/tests/system/geoip2/ns2/example.db.in create mode 100644 bin/tests/system/geoip2/ns2/named1.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named10.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named11.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named12.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named2.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named3.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named4.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named5.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named6.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named7.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named8.conf.in create mode 100644 bin/tests/system/geoip2/ns2/named9.conf.in create mode 100644 bin/tests/system/geoip2/prereq.sh create mode 100644 bin/tests/system/geoip2/setup.sh create mode 100644 bin/tests/system/geoip2/tests.sh create mode 100644 bin/tests/system/geoip2/tests_sh_geoip2.py create mode 100755 bin/tests/system/get_algorithms.py create mode 100755 bin/tests/system/get_core_dumps.sh create mode 100755 bin/tests/system/get_ports.sh create mode 100644 bin/tests/system/glue/clean.sh create mode 100644 bin/tests/system/glue/fi.good create mode 100644 bin/tests/system/glue/noglue.good create mode 100644 bin/tests/system/glue/ns1/named.conf.in create mode 100644 bin/tests/system/glue/ns1/net.db create mode 100644 bin/tests/system/glue/ns1/root-servers.nil.db create mode 100644 bin/tests/system/glue/ns1/root.db create mode 100644 bin/tests/system/glue/setup.sh create mode 100644 bin/tests/system/glue/tests.sh create mode 100644 bin/tests/system/glue/tests_sh_glue.py create mode 100644 bin/tests/system/hooks/clean.sh create mode 100644 bin/tests/system/hooks/driver/Makefile.am create mode 100644 bin/tests/system/hooks/driver/Makefile.in create mode 100644 bin/tests/system/hooks/driver/test-async.c create mode 100644 bin/tests/system/hooks/ns1/example.db create mode 100644 bin/tests/system/hooks/ns1/named.conf.in create mode 100644 bin/tests/system/hooks/setup.sh create mode 100644 bin/tests/system/hooks/tests_async_plugin.py create mode 100644 bin/tests/system/host/clean.sh create mode 100644 bin/tests/system/host/ns1/example.net.db create mode 100644 bin/tests/system/host/ns1/named.conf.in create mode 100644 bin/tests/system/host/setup.sh create mode 100644 bin/tests/system/host/tests.sh create mode 100644 bin/tests/system/host/tests_sh_host.py create mode 100644 bin/tests/system/idna/clean.sh create mode 100644 bin/tests/system/idna/ns1/named.conf.in create mode 100644 bin/tests/system/idna/ns1/root.db create mode 100644 bin/tests/system/idna/setup.sh create mode 100644 bin/tests/system/idna/tests.sh create mode 100644 bin/tests/system/idna/tests_sh_idna.py create mode 100755 bin/tests/system/ifconfig.sh.in create mode 100644 bin/tests/system/include-multiplecfg/clean.sh create mode 100644 bin/tests/system/include-multiplecfg/ns2/mars.com.db create mode 100644 bin/tests/system/include-multiplecfg/ns2/mars.conf create mode 100644 bin/tests/system/include-multiplecfg/ns2/named.conf.in create mode 100644 bin/tests/system/include-multiplecfg/ns2/zone1.com.db create mode 100644 bin/tests/system/include-multiplecfg/ns2/zone1.conf create mode 100644 bin/tests/system/include-multiplecfg/ns2/zone2.com.db create mode 100644 bin/tests/system/include-multiplecfg/ns2/zone2.conf create mode 100644 bin/tests/system/include-multiplecfg/setup.sh create mode 100644 bin/tests/system/include-multiplecfg/tests.sh create mode 100644 bin/tests/system/include-multiplecfg/tests_sh_include_multiplecfg.py create mode 100644 bin/tests/system/inline/clean.sh create mode 100644 bin/tests/system/inline/ns1/named.conf.in create mode 100644 bin/tests/system/inline/ns1/root.db.in create mode 100644 bin/tests/system/inline/ns1/sign.sh create mode 100644 bin/tests/system/inline/ns2/bits.db.in create mode 100644 bin/tests/system/inline/ns2/named.conf.in create mode 100644 bin/tests/system/inline/ns2/nsec3-loop.db.in create mode 100644 bin/tests/system/inline/ns3/include.db.in create mode 100644 bin/tests/system/inline/ns3/named.conf.in create mode 100644 bin/tests/system/inline/ns3/primary.db.in create mode 100644 bin/tests/system/inline/ns3/primary2.db.in create mode 100644 bin/tests/system/inline/ns3/primary3.db.in create mode 100644 bin/tests/system/inline/ns3/primary4.db.in create mode 100644 bin/tests/system/inline/ns3/primary5.db.in create mode 100644 bin/tests/system/inline/ns3/primary6.db.in create mode 100644 bin/tests/system/inline/ns3/primary7.db.in create mode 100755 bin/tests/system/inline/ns3/sign.sh create mode 100644 bin/tests/system/inline/ns4/named.conf.in create mode 100644 bin/tests/system/inline/ns4/noixfr.db.in create mode 100644 bin/tests/system/inline/ns5/named.conf.post create mode 100644 bin/tests/system/inline/ns5/named.conf.pre create mode 100644 bin/tests/system/inline/ns6/named.conf.in create mode 100644 bin/tests/system/inline/ns7/named.conf.in create mode 100755 bin/tests/system/inline/ns7/sign.sh create mode 100644 bin/tests/system/inline/ns8/example.com.db.in create mode 100644 bin/tests/system/inline/ns8/example.db.in create mode 100644 bin/tests/system/inline/ns8/example2.db.in create mode 100644 bin/tests/system/inline/ns8/example3.db.in create mode 100644 bin/tests/system/inline/ns8/named.conf.in create mode 100755 bin/tests/system/inline/ns8/sign.sh create mode 100644 bin/tests/system/inline/setup.sh create mode 100755 bin/tests/system/inline/tests.sh create mode 100644 bin/tests/system/inline/tests_sh_inline.py create mode 100755 bin/tests/system/inline/tests_signed_zone_files.py create mode 100644 bin/tests/system/integrity/clean.sh create mode 100644 bin/tests/system/integrity/ns1/mx-cname.db create mode 100644 bin/tests/system/integrity/ns1/named.conf.in create mode 100644 bin/tests/system/integrity/ns1/srv-cname.db create mode 100644 bin/tests/system/integrity/setup.sh create mode 100644 bin/tests/system/integrity/tests.sh create mode 100644 bin/tests/system/integrity/tests_sh_integrity.py create mode 100644 bin/tests/system/ixfr/ans2/startme create mode 100644 bin/tests/system/ixfr/clean.sh create mode 100644 bin/tests/system/ixfr/ixfr-stats.good create mode 100644 bin/tests/system/ixfr/ns1/named.conf.in create mode 100644 bin/tests/system/ixfr/ns3/named.conf.in create mode 100644 bin/tests/system/ixfr/ns4/named.conf.in create mode 100644 bin/tests/system/ixfr/ns5/named.conf.in create mode 100644 bin/tests/system/ixfr/setup.sh create mode 100644 bin/tests/system/ixfr/tests.sh create mode 100644 bin/tests/system/ixfr/tests_sh_ixfr.py create mode 100644 bin/tests/system/journal/clean.sh create mode 100644 bin/tests/system/journal/ns1/changed.ver1.jnl.saved create mode 100644 bin/tests/system/journal/ns1/changed.ver2.jnl.saved create mode 100644 bin/tests/system/journal/ns1/d1212.jnl.saved create mode 100644 bin/tests/system/journal/ns1/d2121.jnl.saved create mode 100644 bin/tests/system/journal/ns1/generic.db.in create mode 100644 bin/tests/system/journal/ns1/ixfr.db.in create mode 100644 bin/tests/system/journal/ns1/ixfr.ver1.jnl.saved create mode 100644 bin/tests/system/journal/ns1/managed-keys.bind.in create mode 100644 bin/tests/system/journal/ns1/managed-keys.bind.jnl.in create mode 100644 bin/tests/system/journal/ns1/maxjournal.jnl.saved create mode 100644 bin/tests/system/journal/ns1/maxjournal2.jnl.saved create mode 100644 bin/tests/system/journal/ns1/named.conf.in create mode 100644 bin/tests/system/journal/ns1/unchanged.ver1.jnl.saved create mode 100644 bin/tests/system/journal/ns1/unchanged.ver2.jnl.saved create mode 100644 bin/tests/system/journal/ns2/managed-keys.bind.in create mode 100644 bin/tests/system/journal/ns2/managed-keys.bind.jnl.in create mode 100644 bin/tests/system/journal/ns2/named.conf.in create mode 100644 bin/tests/system/journal/setup.sh create mode 100644 bin/tests/system/journal/tests.sh create mode 100644 bin/tests/system/journal/tests_sh_journal.py create mode 100644 bin/tests/system/kasp.sh create mode 100644 bin/tests/system/kasp/README create mode 100644 bin/tests/system/kasp/clean.sh create mode 100644 bin/tests/system/kasp/kasp.conf create mode 100644 bin/tests/system/kasp/ns2/named.conf.in create mode 100644 bin/tests/system/kasp/ns2/secondary.kasp.db.in create mode 100644 bin/tests/system/kasp/ns2/secondary.kasp.db.in2 create mode 100644 bin/tests/system/kasp/ns2/setup.sh create mode 100644 bin/tests/system/kasp/ns2/template.tld.db.in create mode 100644 bin/tests/system/kasp/ns3/ed25519.conf create mode 100644 bin/tests/system/kasp/ns3/ed448.conf create mode 100644 bin/tests/system/kasp/ns3/named-fips.conf.in create mode 100644 bin/tests/system/kasp/ns3/named.conf.in create mode 100644 bin/tests/system/kasp/ns3/policies/autosign.conf.in create mode 100644 bin/tests/system/kasp/ns3/policies/kasp-fips.conf.in create mode 100644 bin/tests/system/kasp/ns3/policies/kasp.conf.in create mode 100644 bin/tests/system/kasp/ns3/setup.sh create mode 100644 bin/tests/system/kasp/ns3/template.db.in create mode 100644 bin/tests/system/kasp/ns3/template2.db.in create mode 100644 bin/tests/system/kasp/ns4/example1.db.in create mode 100644 bin/tests/system/kasp/ns4/example2.db.in create mode 100644 bin/tests/system/kasp/ns4/named.conf.in create mode 100644 bin/tests/system/kasp/ns4/setup.sh create mode 100644 bin/tests/system/kasp/ns4/template.db.in create mode 100644 bin/tests/system/kasp/ns5/named.conf.in create mode 100644 bin/tests/system/kasp/ns5/setup.sh create mode 100644 bin/tests/system/kasp/ns5/template.db.in create mode 100644 bin/tests/system/kasp/ns6/example.db.in create mode 100644 bin/tests/system/kasp/ns6/example2.db.in create mode 100644 bin/tests/system/kasp/ns6/example3.db.in create mode 100644 bin/tests/system/kasp/ns6/named.conf.in create mode 100644 bin/tests/system/kasp/ns6/named2.conf.in create mode 100644 bin/tests/system/kasp/ns6/policies/csk1.conf.in create mode 100644 bin/tests/system/kasp/ns6/policies/csk2.conf.in create mode 100644 bin/tests/system/kasp/ns6/policies/kasp-fips.conf.in create mode 100644 bin/tests/system/kasp/ns6/policies/kasp.conf.in create mode 100644 bin/tests/system/kasp/ns6/setup.sh create mode 100644 bin/tests/system/kasp/ns6/template.db.in create mode 100644 bin/tests/system/kasp/setup.sh create mode 100644 bin/tests/system/kasp/tests.sh create mode 100644 bin/tests/system/kasp/tests_sh_kasp.py create mode 100644 bin/tests/system/keepalive/clean.sh create mode 100644 bin/tests/system/keepalive/expected create mode 100644 bin/tests/system/keepalive/ns1/named.conf.in create mode 100644 bin/tests/system/keepalive/ns1/root.db create mode 100644 bin/tests/system/keepalive/ns2/example.db create mode 100644 bin/tests/system/keepalive/ns2/named.conf.in create mode 100644 bin/tests/system/keepalive/ns3/named.conf.in create mode 100644 bin/tests/system/keepalive/setup.sh create mode 100644 bin/tests/system/keepalive/tests.sh create mode 100644 bin/tests/system/keepalive/tests_sh_keepalive.py create mode 100644 bin/tests/system/keyfromlabel/clean.sh create mode 100644 bin/tests/system/keyfromlabel/prereq.sh create mode 100644 bin/tests/system/keyfromlabel/setup.sh create mode 100644 bin/tests/system/keyfromlabel/template.db.in create mode 100644 bin/tests/system/keyfromlabel/tests.sh create mode 100644 bin/tests/system/keyfromlabel/tests_sh_keyfromlabel.py create mode 100644 bin/tests/system/keymgr2kasp/README create mode 100644 bin/tests/system/keymgr2kasp/clean.sh create mode 100644 bin/tests/system/keymgr2kasp/ns3/kasp.conf.in create mode 100644 bin/tests/system/keymgr2kasp/ns3/named.conf.in create mode 100644 bin/tests/system/keymgr2kasp/ns3/named2.conf.in create mode 100644 bin/tests/system/keymgr2kasp/ns3/setup.sh create mode 100644 bin/tests/system/keymgr2kasp/ns3/template.db.in create mode 100644 bin/tests/system/keymgr2kasp/ns4/named.conf.in create mode 100644 bin/tests/system/keymgr2kasp/ns4/named2.conf.in create mode 100644 bin/tests/system/keymgr2kasp/ns4/setup.sh create mode 100644 bin/tests/system/keymgr2kasp/ns4/template.ext.db.in create mode 100644 bin/tests/system/keymgr2kasp/ns4/template.int.db.in create mode 100644 bin/tests/system/keymgr2kasp/setup.sh create mode 100644 bin/tests/system/keymgr2kasp/tests.sh create mode 100644 bin/tests/system/keymgr2kasp/tests_sh_keymgr2kasp.py create mode 100644 bin/tests/system/legacy.run.sh.in create mode 100644 bin/tests/system/legacy/build.sh create mode 100644 bin/tests/system/legacy/clean.sh create mode 100644 bin/tests/system/legacy/ns1/named1.conf.in create mode 100644 bin/tests/system/legacy/ns1/named2.conf.in create mode 100644 bin/tests/system/legacy/ns1/root.db create mode 100644 bin/tests/system/legacy/ns1/trusted.conf create mode 100644 bin/tests/system/legacy/ns10/ednsrefused.db create mode 100644 bin/tests/system/legacy/ns10/named.conf.in create mode 100644 bin/tests/system/legacy/ns10/named.ednsrefused create mode 100644 bin/tests/system/legacy/ns2/dropedns.db create mode 100644 bin/tests/system/legacy/ns2/named.conf.in create mode 100644 bin/tests/system/legacy/ns2/named.dropedns create mode 100644 bin/tests/system/legacy/ns3/dropedns-notcp.db create mode 100644 bin/tests/system/legacy/ns3/named.conf.in create mode 100644 bin/tests/system/legacy/ns3/named.dropedns create mode 100644 bin/tests/system/legacy/ns3/named.notcp create mode 100644 bin/tests/system/legacy/ns4/named.args create mode 100644 bin/tests/system/legacy/ns4/named.conf.in create mode 100644 bin/tests/system/legacy/ns4/plain.db create mode 100644 bin/tests/system/legacy/ns5/named.args create mode 100644 bin/tests/system/legacy/ns5/named.conf.in create mode 100644 bin/tests/system/legacy/ns5/named.notcp create mode 100644 bin/tests/system/legacy/ns5/plain-notcp.db create mode 100644 bin/tests/system/legacy/ns6/edns512.db.in create mode 100644 bin/tests/system/legacy/ns6/edns512.db.signed create mode 100644 bin/tests/system/legacy/ns6/named.args create mode 100644 bin/tests/system/legacy/ns6/named.conf.in create mode 100755 bin/tests/system/legacy/ns6/sign.sh create mode 100644 bin/tests/system/legacy/ns7/edns512-notcp.db.in create mode 100644 bin/tests/system/legacy/ns7/edns512-notcp.db.signed create mode 100644 bin/tests/system/legacy/ns7/named.args create mode 100644 bin/tests/system/legacy/ns7/named.conf.in create mode 100644 bin/tests/system/legacy/ns7/named.notcp create mode 100755 bin/tests/system/legacy/ns7/sign.sh create mode 100644 bin/tests/system/legacy/ns8/ednsformerr.db create mode 100644 bin/tests/system/legacy/ns8/named.conf.in create mode 100644 bin/tests/system/legacy/ns8/named.ednsformerr create mode 100644 bin/tests/system/legacy/ns9/ednsnotimp.db create mode 100644 bin/tests/system/legacy/ns9/named.conf.in create mode 100644 bin/tests/system/legacy/ns9/named.ednsnotimp create mode 100644 bin/tests/system/legacy/setup.sh create mode 100755 bin/tests/system/legacy/tests.sh create mode 100644 bin/tests/system/legacy/tests_sh_legacy.py create mode 100644 bin/tests/system/limits/clean.sh create mode 100644 bin/tests/system/limits/knowngood.dig.out.1000 create mode 100644 bin/tests/system/limits/knowngood.dig.out.2000 create mode 100644 bin/tests/system/limits/knowngood.dig.out.3000 create mode 100644 bin/tests/system/limits/knowngood.dig.out.4000 create mode 100644 bin/tests/system/limits/knowngood.dig.out.a-maximum-rrset create mode 100644 bin/tests/system/limits/ns1/example.db create mode 100644 bin/tests/system/limits/ns1/named.conf.in create mode 100644 bin/tests/system/limits/ns1/root.db create mode 100644 bin/tests/system/limits/setup.sh create mode 100644 bin/tests/system/limits/tests.sh create mode 100644 bin/tests/system/limits/tests_sh_limits.py create mode 100644 bin/tests/system/logfileconfig/clean.sh create mode 100644 bin/tests/system/logfileconfig/named1.args create mode 100644 bin/tests/system/logfileconfig/named2.args create mode 100644 bin/tests/system/logfileconfig/ns1/named.abspathconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.dirconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.incconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.iso8601-utc.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.iso8601.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.pipeconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.plain.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.plainconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.symconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.tsconf.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.unlimited.in create mode 100644 bin/tests/system/logfileconfig/ns1/named.versconf.in create mode 100644 bin/tests/system/logfileconfig/setup.sh create mode 100644 bin/tests/system/logfileconfig/tests.sh create mode 100644 bin/tests/system/logfileconfig/tests_sh_logfileconfig.py create mode 100644 bin/tests/system/makejournal.c create mode 100644 bin/tests/system/masterfile/clean.sh create mode 100644 bin/tests/system/masterfile/knowngood.dig.out create mode 100644 bin/tests/system/masterfile/ns1/include.db create mode 100644 bin/tests/system/masterfile/ns1/named.conf.in create mode 100644 bin/tests/system/masterfile/ns1/sub.db create mode 100644 bin/tests/system/masterfile/ns1/ttl1.db create mode 100644 bin/tests/system/masterfile/ns1/ttl2.db create mode 100644 bin/tests/system/masterfile/ns2/example.db create mode 100644 bin/tests/system/masterfile/ns2/named.conf.in create mode 100644 bin/tests/system/masterfile/setup.sh create mode 100644 bin/tests/system/masterfile/tests.sh create mode 100644 bin/tests/system/masterfile/tests_sh_masterfile.py create mode 100644 bin/tests/system/masterfile/zone/inheritownerafterinclude.db create mode 100644 bin/tests/system/masterfile/zone/inheritownerafterinclude.good create mode 100644 bin/tests/system/masterfile/zone/nameservers.db create mode 100755 bin/tests/system/masterformat/clean.sh create mode 100755 bin/tests/system/masterformat/ns1/compile.sh create mode 100644 bin/tests/system/masterformat/ns1/example.db create mode 100644 bin/tests/system/masterformat/ns1/large.db.in create mode 100644 bin/tests/system/masterformat/ns1/named.conf.in create mode 100644 bin/tests/system/masterformat/ns1/signed.db create mode 100644 bin/tests/system/masterformat/ns2/formerly-text.db.in create mode 100644 bin/tests/system/masterformat/ns2/named.conf.in create mode 100644 bin/tests/system/masterformat/ns3/named.conf.in create mode 100755 bin/tests/system/masterformat/setup.sh create mode 100755 bin/tests/system/masterformat/tests.sh create mode 100644 bin/tests/system/masterformat/tests_sh_masterformat.py create mode 100644 bin/tests/system/metadata/child.db create mode 100644 bin/tests/system/metadata/clean.sh create mode 100644 bin/tests/system/metadata/parent.db create mode 100644 bin/tests/system/metadata/setup.sh create mode 100644 bin/tests/system/metadata/tests.sh create mode 100644 bin/tests/system/metadata/tests_sh_metadata.py create mode 100644 bin/tests/system/mirror/README create mode 100644 bin/tests/system/mirror/clean.sh create mode 100644 bin/tests/system/mirror/ns1/named.conf.in create mode 100644 bin/tests/system/mirror/ns1/root.db.in create mode 100644 bin/tests/system/mirror/ns1/sign.sh create mode 100644 bin/tests/system/mirror/ns2/example.db.in create mode 100644 bin/tests/system/mirror/ns2/initially-unavailable.db.in create mode 100644 bin/tests/system/mirror/ns2/named.conf.in create mode 100644 bin/tests/system/mirror/ns2/sign.sh create mode 100644 bin/tests/system/mirror/ns2/sub.example.db.in create mode 100644 bin/tests/system/mirror/ns2/verify.db.in create mode 100644 bin/tests/system/mirror/ns3/named.args create mode 100644 bin/tests/system/mirror/ns3/named.conf.in create mode 100644 bin/tests/system/mirror/setup.sh create mode 100644 bin/tests/system/mirror/tests.sh create mode 100644 bin/tests/system/mirror/tests_sh_mirror.py create mode 100644 bin/tests/system/mkeys/README create mode 100644 bin/tests/system/mkeys/clean.sh create mode 100644 bin/tests/system/mkeys/ns1/named1.conf.in create mode 100644 bin/tests/system/mkeys/ns1/named2.conf.in create mode 100644 bin/tests/system/mkeys/ns1/named3.conf.in create mode 100644 bin/tests/system/mkeys/ns1/root.db create mode 100644 bin/tests/system/mkeys/ns1/sign.sh create mode 100644 bin/tests/system/mkeys/ns1/sub.tld.db create mode 100644 bin/tests/system/mkeys/ns1/tld.db create mode 100644 bin/tests/system/mkeys/ns1/unsupported.key create mode 100644 bin/tests/system/mkeys/ns2/named.args create mode 100644 bin/tests/system/mkeys/ns2/named.conf.in create mode 100644 bin/tests/system/mkeys/ns3/named.args create mode 100644 bin/tests/system/mkeys/ns3/named.conf.in create mode 100644 bin/tests/system/mkeys/ns4/named.conf.in create mode 100644 bin/tests/system/mkeys/ns4/sign.sh create mode 100644 bin/tests/system/mkeys/ns4/sub.foo.db create mode 100644 bin/tests/system/mkeys/ns5/foo.db create mode 100644 bin/tests/system/mkeys/ns5/named.conf.in create mode 100644 bin/tests/system/mkeys/ns5/named1.args create mode 100644 bin/tests/system/mkeys/ns5/named2.args create mode 100644 bin/tests/system/mkeys/ns6/named.args create mode 100644 bin/tests/system/mkeys/ns6/named.conf.in create mode 100644 bin/tests/system/mkeys/ns6/setup.sh create mode 100644 bin/tests/system/mkeys/ns6/unsupported-managed.key create mode 100644 bin/tests/system/mkeys/ns7/named.conf.in create mode 100644 bin/tests/system/mkeys/setup.sh create mode 100644 bin/tests/system/mkeys/tests.sh create mode 100644 bin/tests/system/mkeys/tests_sh_mkeys.py create mode 100644 bin/tests/system/names/clean.sh create mode 100644 bin/tests/system/names/ns1/example.db create mode 100644 bin/tests/system/names/ns1/named.conf.in create mode 100644 bin/tests/system/names/setup.sh create mode 100644 bin/tests/system/names/tests.sh create mode 100644 bin/tests/system/names/tests_sh_names.py create mode 100644 bin/tests/system/notify/clean.sh create mode 100644 bin/tests/system/notify/ns1/named.conf.in create mode 100644 bin/tests/system/notify/ns1/root.db create mode 100644 bin/tests/system/notify/ns2/example1.db create mode 100644 bin/tests/system/notify/ns2/example2.db create mode 100644 bin/tests/system/notify/ns2/example3.db create mode 100644 bin/tests/system/notify/ns2/example4.db create mode 100644 bin/tests/system/notify/ns2/generic.db create mode 100644 bin/tests/system/notify/ns2/named.conf.in create mode 100644 bin/tests/system/notify/ns3/named.conf.in create mode 100644 bin/tests/system/notify/ns3/notify-source-port-test.db create mode 100644 bin/tests/system/notify/ns4/named.conf.in create mode 100644 bin/tests/system/notify/ns4/named.port.in create mode 100644 bin/tests/system/notify/ns5/named.conf.in create mode 100644 bin/tests/system/notify/ns5/x21.db create mode 100644 bin/tests/system/notify/setup.sh create mode 100644 bin/tests/system/notify/tests.sh create mode 100644 bin/tests/system/notify/tests_sh_notify.py create mode 100644 bin/tests/system/nsec3/clean.sh create mode 100644 bin/tests/system/nsec3/ns2/named.conf.in create mode 100644 bin/tests/system/nsec3/ns2/setup.sh create mode 100644 bin/tests/system/nsec3/ns2/template.db.in create mode 100644 bin/tests/system/nsec3/ns3/named.conf.in create mode 100644 bin/tests/system/nsec3/ns3/named2.conf.in create mode 100644 bin/tests/system/nsec3/ns3/nsec3-fails-to-load.kasp.db.in create mode 100644 bin/tests/system/nsec3/ns3/setup.sh create mode 100644 bin/tests/system/nsec3/ns3/template.db.in create mode 100644 bin/tests/system/nsec3/setup.sh create mode 100644 bin/tests/system/nsec3/tests.sh create mode 100644 bin/tests/system/nsec3/tests_sh_nsec3.py create mode 100644 bin/tests/system/nslookup/clean.sh create mode 100644 bin/tests/system/nslookup/ns1/example.net.db create mode 100644 bin/tests/system/nslookup/ns1/named.conf.in create mode 100644 bin/tests/system/nslookup/setup.sh create mode 100644 bin/tests/system/nslookup/tests.sh create mode 100644 bin/tests/system/nslookup/tests_sh_nslookup.py create mode 100644 bin/tests/system/nsupdate/ans4/ans.pl create mode 100644 bin/tests/system/nsupdate/clean.sh create mode 100644 bin/tests/system/nsupdate/commandlist create mode 100644 bin/tests/system/nsupdate/knowngood.ns1.after create mode 100644 bin/tests/system/nsupdate/knowngood.ns1.afterstop create mode 100644 bin/tests/system/nsupdate/knowngood.ns1.before create mode 100644 bin/tests/system/nsupdate/krb/setup.sh create mode 100644 bin/tests/system/nsupdate/ns1/example1.db create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-157.+157+23571.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-157.+157+23571.private create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-161.+161+23350.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-161.+161+23350.private create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-162.+162+00032.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-162.+162+00032.private create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-163.+163+48857.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-163.+163+48857.private create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-164.+164+09001.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-164.+164+09001.private create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-165.+165+61012.key create mode 100644 bin/tests/system/nsupdate/ns1/legacy/Klegacy-165.+165+61012.private create mode 100644 bin/tests/system/nsupdate/ns1/many.test.db.in create mode 100644 bin/tests/system/nsupdate/ns1/max-ttl.db create mode 100644 bin/tests/system/nsupdate/ns1/maxjournal.db.in create mode 100644 bin/tests/system/nsupdate/ns1/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns1/sample.db.in create mode 100644 bin/tests/system/nsupdate/ns10/dns.keytab create mode 100644 bin/tests/system/nsupdate/ns10/example.com.db.in create mode 100644 bin/tests/system/nsupdate/ns10/in-addr.db.in create mode 100644 bin/tests/system/nsupdate/ns10/machine.ccache create mode 100644 bin/tests/system/nsupdate/ns10/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns2/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns2/sample.db.in create mode 100644 bin/tests/system/nsupdate/ns3/delegation.test.db.in create mode 100644 bin/tests/system/nsupdate/ns3/dnskey.test.db.in create mode 100644 bin/tests/system/nsupdate/ns3/example.db.in create mode 100644 bin/tests/system/nsupdate/ns3/multisigner.test.db.in create mode 100644 bin/tests/system/nsupdate/ns3/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns3/nsec3param.test.db.in create mode 100644 bin/tests/system/nsupdate/ns3/sign.sh create mode 100644 bin/tests/system/nsupdate/ns3/too-big.test.db.in create mode 100644 bin/tests/system/nsupdate/ns5/local.db.in create mode 100644 bin/tests/system/nsupdate/ns5/named.args create mode 100644 bin/tests/system/nsupdate/ns5/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns6/in-addr.db.in create mode 100644 bin/tests/system/nsupdate/ns6/named.args create mode 100644 bin/tests/system/nsupdate/ns6/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns7/dns.keytab create mode 100644 bin/tests/system/nsupdate/ns7/example.com.db.in create mode 100644 bin/tests/system/nsupdate/ns7/in-addr.db.in create mode 100644 bin/tests/system/nsupdate/ns7/machine.ccache create mode 100644 bin/tests/system/nsupdate/ns7/named1.conf.in create mode 100644 bin/tests/system/nsupdate/ns7/named2.conf.in create mode 100644 bin/tests/system/nsupdate/ns8/dns-other-than-KRB5_KTNAME.keytab create mode 100644 bin/tests/system/nsupdate/ns8/example.com.db.in create mode 100644 bin/tests/system/nsupdate/ns8/in-addr.db.in create mode 100644 bin/tests/system/nsupdate/ns8/machine.ccache create mode 100644 bin/tests/system/nsupdate/ns8/named.conf.in create mode 100644 bin/tests/system/nsupdate/ns9/dns.keytab create mode 100644 bin/tests/system/nsupdate/ns9/example.com.db.in create mode 100644 bin/tests/system/nsupdate/ns9/in-addr.db.in create mode 100644 bin/tests/system/nsupdate/ns9/machine.ccache create mode 100644 bin/tests/system/nsupdate/ns9/named.conf.in create mode 100644 bin/tests/system/nsupdate/resolv.conf create mode 100644 bin/tests/system/nsupdate/setup.sh create mode 100755 bin/tests/system/nsupdate/tests.sh create mode 100644 bin/tests/system/nsupdate/tests_sh_nsupdate.py create mode 100644 bin/tests/system/nsupdate/update_test.pl create mode 100644 bin/tests/system/nsupdate/verylarge.in create mode 100644 bin/tests/system/nzd2nzf/clean.sh create mode 100644 bin/tests/system/nzd2nzf/ns1/added.db create mode 100644 bin/tests/system/nzd2nzf/ns1/named.conf.in create mode 100644 bin/tests/system/nzd2nzf/prereq.sh create mode 100644 bin/tests/system/nzd2nzf/setup.sh create mode 100644 bin/tests/system/nzd2nzf/tests.sh create mode 100644 bin/tests/system/nzd2nzf/tests_sh_nzd2nzf.py create mode 100644 bin/tests/system/org.isc.bind.system create mode 100644 bin/tests/system/org.isc.bind.system.plist create mode 100644 bin/tests/system/packet.pl create mode 100644 bin/tests/system/padding/clean.sh create mode 100644 bin/tests/system/padding/ns1/named.conf.in create mode 100644 bin/tests/system/padding/ns1/root.db create mode 100644 bin/tests/system/padding/ns2/example.db create mode 100644 bin/tests/system/padding/ns2/named.conf.in create mode 100644 bin/tests/system/padding/ns3/named.conf.in create mode 100644 bin/tests/system/padding/ns4/named.conf.in create mode 100644 bin/tests/system/padding/setup.sh create mode 100644 bin/tests/system/padding/tests.sh create mode 100644 bin/tests/system/padding/tests_sh_padding.py create mode 100644 bin/tests/system/parallel.sh create mode 100644 bin/tests/system/pending/clean.sh create mode 100644 bin/tests/system/pending/ns1/named.conf.in create mode 100644 bin/tests/system/pending/ns1/root.db.in create mode 100644 bin/tests/system/pending/ns1/sign.sh create mode 100644 bin/tests/system/pending/ns2/example.com.db.in create mode 100644 bin/tests/system/pending/ns2/example.db.in create mode 100644 bin/tests/system/pending/ns2/forgery.db create mode 100644 bin/tests/system/pending/ns2/named.conf.in create mode 100644 bin/tests/system/pending/ns2/sign.sh create mode 100644 bin/tests/system/pending/ns3/hostile.db create mode 100644 bin/tests/system/pending/ns3/mail.example.db create mode 100644 bin/tests/system/pending/ns3/named.conf.in create mode 100644 bin/tests/system/pending/ns4/named.conf.in create mode 100644 bin/tests/system/pending/setup.sh create mode 100644 bin/tests/system/pending/tests.sh create mode 100644 bin/tests/system/pending/tests_sh_pending.py create mode 100644 bin/tests/system/pipelined/ans5/ans.py create mode 100644 bin/tests/system/pipelined/clean.sh create mode 100644 bin/tests/system/pipelined/input create mode 100644 bin/tests/system/pipelined/inputb create mode 100644 bin/tests/system/pipelined/ns1/named.conf.in create mode 100644 bin/tests/system/pipelined/ns1/root.db create mode 100644 bin/tests/system/pipelined/ns2/examplea.db create mode 100644 bin/tests/system/pipelined/ns2/named.conf.in create mode 100644 bin/tests/system/pipelined/ns3/exampleb.db create mode 100644 bin/tests/system/pipelined/ns3/named.conf.in create mode 100644 bin/tests/system/pipelined/ns4/named.conf.in create mode 100644 bin/tests/system/pipelined/pipequeries.c create mode 100644 bin/tests/system/pipelined/ref create mode 100644 bin/tests/system/pipelined/refb create mode 100644 bin/tests/system/pipelined/setup.sh create mode 100644 bin/tests/system/pipelined/tests.sh create mode 100644 bin/tests/system/pipelined/tests_sh_pipelined.py create mode 100644 bin/tests/system/pytest.ini create mode 100644 bin/tests/system/pytest_custom_markers.py create mode 100755 bin/tests/system/qmin/ans2/ans.py create mode 100755 bin/tests/system/qmin/ans3/ans.py create mode 100755 bin/tests/system/qmin/ans4/ans.py create mode 100644 bin/tests/system/qmin/clean.sh create mode 100644 bin/tests/system/qmin/ns1/named.conf.in create mode 100644 bin/tests/system/qmin/ns1/root.db create mode 100644 bin/tests/system/qmin/ns5/named.conf.in create mode 100644 bin/tests/system/qmin/ns6/named.conf.in create mode 100644 bin/tests/system/qmin/ns7/named.conf.in create mode 100644 bin/tests/system/qmin/setup.sh create mode 100755 bin/tests/system/qmin/tests.sh create mode 100644 bin/tests/system/qmin/tests_sh_qmin.py create mode 100644 bin/tests/system/reclimit/README create mode 100644 bin/tests/system/reclimit/ans2/ans.pl create mode 100644 bin/tests/system/reclimit/ans4/ans.pl create mode 100644 bin/tests/system/reclimit/ans7/ans.pl create mode 100644 bin/tests/system/reclimit/clean.sh create mode 100644 bin/tests/system/reclimit/ns1/named.conf.in create mode 100644 bin/tests/system/reclimit/ns1/root.db create mode 100644 bin/tests/system/reclimit/ns3/hints.db create mode 100644 bin/tests/system/reclimit/ns3/named1.conf.in create mode 100644 bin/tests/system/reclimit/ns3/named2.conf.in create mode 100644 bin/tests/system/reclimit/ns3/named3.conf.in create mode 100644 bin/tests/system/reclimit/ns3/named4.conf.in create mode 100644 bin/tests/system/reclimit/setup.sh create mode 100644 bin/tests/system/reclimit/tests.sh create mode 100644 bin/tests/system/reclimit/tests_sh_reclimit.py create mode 100644 bin/tests/system/redirect/clean.sh create mode 100644 bin/tests/system/redirect/conf/bad1.conf create mode 100644 bin/tests/system/redirect/conf/bad2.conf create mode 100644 bin/tests/system/redirect/conf/bad3.conf create mode 100644 bin/tests/system/redirect/conf/good1.conf create mode 100644 bin/tests/system/redirect/conf/good2.conf create mode 100644 bin/tests/system/redirect/conf/good3.conf create mode 100644 bin/tests/system/redirect/conf/good4.conf create mode 100644 bin/tests/system/redirect/ns1/example.db create mode 100644 bin/tests/system/redirect/ns1/named.conf.in create mode 100644 bin/tests/system/redirect/ns1/redirect.db create mode 100644 bin/tests/system/redirect/ns1/root.db create mode 100644 bin/tests/system/redirect/ns1/sign.sh create mode 100644 bin/tests/system/redirect/ns2/example.db.in create mode 100644 bin/tests/system/redirect/ns2/named.conf.in create mode 100644 bin/tests/system/redirect/ns2/redirect.db.in create mode 100644 bin/tests/system/redirect/ns3/example.db create mode 100644 bin/tests/system/redirect/ns3/named.conf.in create mode 100644 bin/tests/system/redirect/ns3/redirect.db create mode 100644 bin/tests/system/redirect/ns3/root.db create mode 100644 bin/tests/system/redirect/ns3/sign.sh create mode 100644 bin/tests/system/redirect/ns4/example.db.in create mode 100644 bin/tests/system/redirect/ns4/named.conf.in create mode 100644 bin/tests/system/redirect/ns4/root.hint create mode 100644 bin/tests/system/redirect/ns5/named.conf.in create mode 100644 bin/tests/system/redirect/ns5/root.db.in create mode 100644 bin/tests/system/redirect/ns5/sign.sh create mode 100644 bin/tests/system/redirect/ns5/signed.db.in create mode 100644 bin/tests/system/redirect/ns5/unsigned.db create mode 100644 bin/tests/system/redirect/ns6/named.conf.in create mode 100644 bin/tests/system/redirect/ns6/root.db create mode 100644 bin/tests/system/redirect/setup.sh create mode 100644 bin/tests/system/redirect/tests.sh create mode 100644 bin/tests/system/redirect/tests_sh_redirect.py create mode 100644 bin/tests/system/resolve.c create mode 100644 bin/tests/system/resolver/ans10/ans.py create mode 100644 bin/tests/system/resolver/ans2/ans.pl create mode 100644 bin/tests/system/resolver/ans3/ans.pl create mode 100644 bin/tests/system/resolver/ans8/ans.pl create mode 100644 bin/tests/system/resolver/clean.sh create mode 100644 bin/tests/system/resolver/ns1/chaostest.db create mode 100644 bin/tests/system/resolver/ns1/named.conf.in create mode 100644 bin/tests/system/resolver/ns1/root.hint create mode 100644 bin/tests/system/resolver/ns4/broken.db create mode 100644 bin/tests/system/resolver/ns4/child.server.db create mode 100644 bin/tests/system/resolver/ns4/moves.db create mode 100644 bin/tests/system/resolver/ns4/named.conf.in create mode 100644 bin/tests/system/resolver/ns4/named.noaa create mode 100644 bin/tests/system/resolver/ns4/root.db create mode 100644 bin/tests/system/resolver/ns4/sourcens.db create mode 100644 bin/tests/system/resolver/ns4/tld1.db create mode 100644 bin/tests/system/resolver/ns4/tld2.db create mode 100644 bin/tests/system/resolver/ns4/v4only.net.db create mode 100644 bin/tests/system/resolver/ns5/child.server.db create mode 100644 bin/tests/system/resolver/ns5/moves.db create mode 100644 bin/tests/system/resolver/ns5/named.conf.in create mode 100644 bin/tests/system/resolver/ns5/root.hint create mode 100644 bin/tests/system/resolver/ns6/broken.db create mode 100644 bin/tests/system/resolver/ns6/delegation-only.db create mode 100644 bin/tests/system/resolver/ns6/ds.example.net.db.in create mode 100644 bin/tests/system/resolver/ns6/example.net.db.in create mode 100644 bin/tests/system/resolver/ns6/fetch.tld.db create mode 100644 bin/tests/system/resolver/ns6/keygen.sh create mode 100644 bin/tests/system/resolver/ns6/moves.db create mode 100644 bin/tests/system/resolver/ns6/named.conf.in create mode 100644 bin/tests/system/resolver/ns6/no-edns-version.tld.db create mode 100644 bin/tests/system/resolver/ns6/redirect.com.db create mode 100644 bin/tests/system/resolver/ns6/root.db create mode 100644 bin/tests/system/resolver/ns6/targetns.db create mode 100644 bin/tests/system/resolver/ns6/tld1.db create mode 100644 bin/tests/system/resolver/ns6/to-be-removed.tld.db.in create mode 100644 bin/tests/system/resolver/ns7/all-cnames.db create mode 100644 bin/tests/system/resolver/ns7/edns-version.tld.db create mode 100644 bin/tests/system/resolver/ns7/named1.conf.in create mode 100644 bin/tests/system/resolver/ns7/named2.conf.in create mode 100644 bin/tests/system/resolver/ns7/root.hint create mode 100644 bin/tests/system/resolver/ns7/server.db.in create mode 100644 bin/tests/system/resolver/ns7/sub.tld1.db create mode 100644 bin/tests/system/resolver/ns7/tld2.db create mode 100644 bin/tests/system/resolver/ns9/named.args create mode 100644 bin/tests/system/resolver/ns9/named.conf.in create mode 100644 bin/tests/system/resolver/ns9/named.ipv6-only create mode 100644 bin/tests/system/resolver/ns9/root.hint create mode 100644 bin/tests/system/resolver/setup.sh create mode 100755 bin/tests/system/resolver/tests.sh create mode 100644 bin/tests/system/resolver/tests_sh_resolver.py create mode 100644 bin/tests/system/rndc/clean.sh create mode 100644 bin/tests/system/rndc/gencheck.c create mode 100644 bin/tests/system/rndc/ns2/incl.db create mode 100644 bin/tests/system/rndc/ns2/named.conf.in create mode 100644 bin/tests/system/rndc/ns2/secondkey.conf create mode 100644 bin/tests/system/rndc/ns3/named.conf.in create mode 100644 bin/tests/system/rndc/ns4/named.conf.in create mode 100644 bin/tests/system/rndc/ns5/named.conf.in create mode 100644 bin/tests/system/rndc/ns6/named.args create mode 100644 bin/tests/system/rndc/ns6/named.conf.in create mode 100644 bin/tests/system/rndc/ns7/include.db.in create mode 100644 bin/tests/system/rndc/ns7/include2.db.in create mode 100644 bin/tests/system/rndc/ns7/named.conf.in create mode 100644 bin/tests/system/rndc/ns7/test.db.in create mode 100644 bin/tests/system/rndc/setup.sh create mode 100644 bin/tests/system/rndc/tests.sh create mode 100644 bin/tests/system/rndc/tests_sh_rndc.py create mode 100644 bin/tests/system/rootkeysentinel/clean.sh create mode 100644 bin/tests/system/rootkeysentinel/ns1/named.conf.in create mode 100644 bin/tests/system/rootkeysentinel/ns1/root.db.in create mode 100644 bin/tests/system/rootkeysentinel/ns1/sign.sh create mode 100644 bin/tests/system/rootkeysentinel/ns2/example.db.in create mode 100644 bin/tests/system/rootkeysentinel/ns2/named.conf.in create mode 100644 bin/tests/system/rootkeysentinel/ns2/sign.sh create mode 100644 bin/tests/system/rootkeysentinel/ns3/hint.db create mode 100644 bin/tests/system/rootkeysentinel/ns3/named.conf.in create mode 100644 bin/tests/system/rootkeysentinel/ns4/hint.db create mode 100644 bin/tests/system/rootkeysentinel/ns4/named.conf.in create mode 100644 bin/tests/system/rootkeysentinel/setup.sh create mode 100644 bin/tests/system/rootkeysentinel/tests.sh create mode 100644 bin/tests/system/rootkeysentinel/tests_sh_rootkeysentinel.py create mode 100644 bin/tests/system/rpz/README create mode 100644 bin/tests/system/rpz/clean.sh create mode 100644 bin/tests/system/rpz/dnsrps.c create mode 100644 bin/tests/system/rpz/dnsrpzd-license.conf create mode 100644 bin/tests/system/rpz/dnsrpzd.conf.in create mode 100644 bin/tests/system/rpz/ns1/named.conf.in create mode 100644 bin/tests/system/rpz/ns1/root.db create mode 100644 bin/tests/system/rpz/ns10/hints create mode 100644 bin/tests/system/rpz/ns10/named.conf.in create mode 100644 bin/tests/system/rpz/ns10/stub.db create mode 100644 bin/tests/system/rpz/ns2/base-tld2s.db create mode 100644 bin/tests/system/rpz/ns2/bl.tld2.db.in create mode 100644 bin/tests/system/rpz/ns2/blv2.tld2.db.in create mode 100644 bin/tests/system/rpz/ns2/blv3.tld2.db.in create mode 100644 bin/tests/system/rpz/ns2/hints create mode 100644 bin/tests/system/rpz/ns2/named.conf.in create mode 100644 bin/tests/system/rpz/ns2/stub.db create mode 100644 bin/tests/system/rpz/ns2/tld2.db create mode 100644 bin/tests/system/rpz/ns3/base.db create mode 100644 bin/tests/system/rpz/ns3/broken.db.in create mode 100644 bin/tests/system/rpz/ns3/crash1 create mode 100644 bin/tests/system/rpz/ns3/crash2 create mode 100644 bin/tests/system/rpz/ns3/hints create mode 100644 bin/tests/system/rpz/ns3/manual-update-rpz-2.db.in create mode 100644 bin/tests/system/rpz/ns3/manual-update-rpz.db.in create mode 100644 bin/tests/system/rpz/ns3/mixed-case-rpz-1.db.in create mode 100644 bin/tests/system/rpz/ns3/mixed-case-rpz-2.db.in create mode 100644 bin/tests/system/rpz/ns3/named.conf.in create mode 100644 bin/tests/system/rpz/ns4/hints create mode 100644 bin/tests/system/rpz/ns4/named.conf.in create mode 100644 bin/tests/system/rpz/ns4/tld4.db create mode 100644 bin/tests/system/rpz/ns5/empty.db.in create mode 100644 bin/tests/system/rpz/ns5/expire.conf.in create mode 100644 bin/tests/system/rpz/ns5/fast-expire.db.in create mode 100644 bin/tests/system/rpz/ns5/hints create mode 100644 bin/tests/system/rpz/ns5/named.args create mode 100644 bin/tests/system/rpz/ns5/named.conf.in create mode 100644 bin/tests/system/rpz/ns5/tld5.db create mode 100644 bin/tests/system/rpz/ns6/bl.tld2s.db.in create mode 100644 bin/tests/system/rpz/ns6/hints create mode 100644 bin/tests/system/rpz/ns6/named.conf.in create mode 100644 bin/tests/system/rpz/ns7/hints create mode 100644 bin/tests/system/rpz/ns7/named.conf.in create mode 100644 bin/tests/system/rpz/ns8/hints create mode 100644 bin/tests/system/rpz/ns8/manual-update-rpz.db.in create mode 100644 bin/tests/system/rpz/ns8/named.conf.in create mode 100644 bin/tests/system/rpz/ns9/hints create mode 100644 bin/tests/system/rpz/ns9/named.conf.in create mode 100644 bin/tests/system/rpz/ns9/rpz.db create mode 100644 bin/tests/system/rpz/qperf.sh create mode 100644 bin/tests/system/rpz/setup.sh create mode 100644 bin/tests/system/rpz/test1 create mode 100644 bin/tests/system/rpz/test2 create mode 100644 bin/tests/system/rpz/test3 create mode 100644 bin/tests/system/rpz/test4 create mode 100644 bin/tests/system/rpz/test4a create mode 100644 bin/tests/system/rpz/test5 create mode 100644 bin/tests/system/rpz/test6 create mode 100644 bin/tests/system/rpz/tests.sh create mode 100644 bin/tests/system/rpz/tests_sh_rpz.py create mode 100644 bin/tests/system/rpzextra/clean.sh create mode 100644 bin/tests/system/rpzextra/ns2/allowed.db create mode 100644 bin/tests/system/rpzextra/ns2/baddomain.db create mode 100644 bin/tests/system/rpzextra/ns2/gooddomain.db create mode 100644 bin/tests/system/rpzextra/ns2/named.conf.in create mode 100644 bin/tests/system/rpzextra/ns2/rpz-external.local.db create mode 100644 bin/tests/system/rpzextra/ns3/external-rpz.local.db create mode 100644 bin/tests/system/rpzextra/ns3/first-rpz.local.db create mode 100644 bin/tests/system/rpzextra/ns3/fourth-rpz-extra.local.db create mode 100644 bin/tests/system/rpzextra/ns3/named.args create mode 100644 bin/tests/system/rpzextra/ns3/named.conf.in create mode 100644 bin/tests/system/rpzextra/ns3/root.db create mode 100644 bin/tests/system/rpzextra/ns3/third-rpz-extra.local.db create mode 100644 bin/tests/system/rpzextra/setup.sh create mode 100644 bin/tests/system/rpzextra/tests_rpzextra.py create mode 100644 bin/tests/system/rpzrecurse/README create mode 100644 bin/tests/system/rpzrecurse/ans5/ans.pl create mode 100644 bin/tests/system/rpzrecurse/clean.sh create mode 100644 bin/tests/system/rpzrecurse/ns1/db.l0 create mode 100644 bin/tests/system/rpzrecurse/ns1/db.l1.l0 create mode 100644 bin/tests/system/rpzrecurse/ns1/example.com.db create mode 100644 bin/tests/system/rpzrecurse/ns1/example.db create mode 100644 bin/tests/system/rpzrecurse/ns1/named.conf.in create mode 100644 bin/tests/system/rpzrecurse/ns1/root.db create mode 100644 bin/tests/system/rpzrecurse/ns1/test1.example.net.db create mode 100644 bin/tests/system/rpzrecurse/ns1/test2.example.net.db create mode 100644 bin/tests/system/rpzrecurse/ns2/db.clientip1 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.clientip2 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.clientip21 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.given create mode 100644 bin/tests/system/rpzrecurse/ns2/db.invalidprefixlength create mode 100644 bin/tests/system/rpzrecurse/ns2/db.log1 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.log2 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.log3 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.passthru create mode 100644 bin/tests/system/rpzrecurse/ns2/db.wildcard1 create mode 100644 bin/tests/system/rpzrecurse/ns2/db.wildcard2a create mode 100644 bin/tests/system/rpzrecurse/ns2/db.wildcard2b create mode 100644 bin/tests/system/rpzrecurse/ns2/db.wildcard3 create mode 100644 bin/tests/system/rpzrecurse/ns2/named.clientip.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.clientip2.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.conf.header.in create mode 100644 bin/tests/system/rpzrecurse/ns2/named.default.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.invalidprefixlength.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.log.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.max.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/named.wildcard4.conf create mode 100644 bin/tests/system/rpzrecurse/ns2/root.hint create mode 100644 bin/tests/system/rpzrecurse/ns3/example.db create mode 100644 bin/tests/system/rpzrecurse/ns3/named1.conf.in create mode 100644 bin/tests/system/rpzrecurse/ns3/named2.conf.in create mode 100644 bin/tests/system/rpzrecurse/ns3/named3.conf.in create mode 100644 bin/tests/system/rpzrecurse/ns3/policy.db create mode 100644 bin/tests/system/rpzrecurse/ns3/root.db create mode 100644 bin/tests/system/rpzrecurse/ns4/child.example.db create mode 100644 bin/tests/system/rpzrecurse/ns4/named.conf.in create mode 100644 bin/tests/system/rpzrecurse/setup.sh create mode 100755 bin/tests/system/rpzrecurse/testgen.pl create mode 100644 bin/tests/system/rpzrecurse/tests.sh create mode 100644 bin/tests/system/rpzrecurse/tests_sh_rpzrecurse.py create mode 100644 bin/tests/system/rrchecker/classlist.good create mode 100644 bin/tests/system/rrchecker/clean.sh create mode 100644 bin/tests/system/rrchecker/privatelist.good create mode 100644 bin/tests/system/rrchecker/tests.sh create mode 100644 bin/tests/system/rrchecker/tests_sh_rrchecker.py create mode 100644 bin/tests/system/rrchecker/typelist.good create mode 100644 bin/tests/system/rrl/broken.conf.in create mode 100644 bin/tests/system/rrl/clean.sh create mode 100644 bin/tests/system/rrl/ns1/named.conf.in create mode 100644 bin/tests/system/rrl/ns1/root.db create mode 100644 bin/tests/system/rrl/ns2/hints create mode 100644 bin/tests/system/rrl/ns2/named.conf.in create mode 100644 bin/tests/system/rrl/ns2/tld2.db create mode 100644 bin/tests/system/rrl/ns3/hints create mode 100644 bin/tests/system/rrl/ns3/named.conf.in create mode 100644 bin/tests/system/rrl/ns3/tld3.db create mode 100644 bin/tests/system/rrl/ns4/hints create mode 100644 bin/tests/system/rrl/ns4/named.conf.in create mode 100644 bin/tests/system/rrl/ns4/tld4.db create mode 100644 bin/tests/system/rrl/setup.sh create mode 100644 bin/tests/system/rrl/tests.sh create mode 100644 bin/tests/system/rrl/tests_sh_rrl.py create mode 100644 bin/tests/system/rrsetorder/clean.sh create mode 100644 bin/tests/system/rrsetorder/ns1/named.conf.in create mode 100644 bin/tests/system/rrsetorder/ns1/root.db create mode 100644 bin/tests/system/rrsetorder/ns2/named.conf.in create mode 100644 bin/tests/system/rrsetorder/ns3/named.conf.in create mode 100644 bin/tests/system/rrsetorder/ns4/named.conf.in create mode 100644 bin/tests/system/rrsetorder/ns5/named.conf.in create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.fixed.good create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good1 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good10 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good11 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good12 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good13 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good14 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good15 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good16 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good17 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good18 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good19 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good2 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good20 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good21 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good22 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good23 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good24 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good3 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good4 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good5 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good6 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good7 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good8 create mode 100644 bin/tests/system/rrsetorder/reference.dig.out.random.good9 create mode 100644 bin/tests/system/rrsetorder/setup.sh create mode 100644 bin/tests/system/rrsetorder/tests.sh create mode 100644 bin/tests/system/rrsetorder/tests_sh_rrsetorder.py create mode 100644 bin/tests/system/rsabigexponent/README.md create mode 100644 bin/tests/system/rsabigexponent/bigkey.c create mode 100644 bin/tests/system/rsabigexponent/clean.sh create mode 100644 bin/tests/system/rsabigexponent/conf/bad01.conf create mode 100644 bin/tests/system/rsabigexponent/conf/bad02.conf create mode 100644 bin/tests/system/rsabigexponent/conf/bad03.conf create mode 100644 bin/tests/system/rsabigexponent/conf/good01.conf create mode 100644 bin/tests/system/rsabigexponent/conf/good02.conf create mode 100644 bin/tests/system/rsabigexponent/conf/good03.conf create mode 100644 bin/tests/system/rsabigexponent/ns1/named.conf.in create mode 100644 bin/tests/system/rsabigexponent/ns1/root.db.in create mode 100755 bin/tests/system/rsabigexponent/ns1/sign.sh create mode 100644 bin/tests/system/rsabigexponent/ns2/Xexample.+008+51650.key create mode 100644 bin/tests/system/rsabigexponent/ns2/Xexample.+008+51650.private create mode 100644 bin/tests/system/rsabigexponent/ns2/Xexample.+008+52810.key create mode 100644 bin/tests/system/rsabigexponent/ns2/Xexample.+008+52810.private create mode 100644 bin/tests/system/rsabigexponent/ns2/dsset-example.in create mode 100644 bin/tests/system/rsabigexponent/ns2/example.db.bad create mode 100644 bin/tests/system/rsabigexponent/ns2/example.db.in create mode 100644 bin/tests/system/rsabigexponent/ns2/named.conf.in create mode 100755 bin/tests/system/rsabigexponent/ns2/sign.sh create mode 100644 bin/tests/system/rsabigexponent/ns3/named.conf.in create mode 100644 bin/tests/system/rsabigexponent/setup.sh create mode 100644 bin/tests/system/rsabigexponent/tests.sh create mode 100644 bin/tests/system/rsabigexponent/tests_sh_rsabigexponent.py create mode 100644 bin/tests/system/run.gdb create mode 100755 bin/tests/system/run.sh create mode 100755 bin/tests/system/runall.sh create mode 100755 bin/tests/system/runsequential.sh create mode 100644 bin/tests/system/runtime/README create mode 100644 bin/tests/system/runtime/clean.sh create mode 100644 bin/tests/system/runtime/ctrl-chars create mode 100644 bin/tests/system/runtime/long-cmd-line create mode 100644 bin/tests/system/runtime/ns2/named-alt1.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt2.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt3.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt4.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt5.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt6.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt7.conf.in create mode 100644 bin/tests/system/runtime/ns2/named-alt9.conf.in create mode 100644 bin/tests/system/runtime/ns2/named1.conf.in create mode 100644 bin/tests/system/runtime/setup.sh create mode 100644 bin/tests/system/runtime/tests.sh create mode 100644 bin/tests/system/runtime/tests_sh_runtime.py create mode 100644 bin/tests/system/send.pl create mode 100644 bin/tests/system/serve-stale/ans2/ans.pl create mode 100644 bin/tests/system/serve-stale/clean.sh create mode 100644 bin/tests/system/serve-stale/ns1/named1.conf.in create mode 100644 bin/tests/system/serve-stale/ns1/named2.conf.in create mode 100644 bin/tests/system/serve-stale/ns1/named3.conf.in create mode 100644 bin/tests/system/serve-stale/ns1/named4.conf.in create mode 100644 bin/tests/system/serve-stale/ns1/root.db create mode 100644 bin/tests/system/serve-stale/ns1/stale.test.db create mode 100644 bin/tests/system/serve-stale/ns3/named1.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named2.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named3.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named4.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named5.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named6.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named7.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/named8.conf.in create mode 100644 bin/tests/system/serve-stale/ns3/root.db create mode 100644 bin/tests/system/serve-stale/ns4/named.conf.in create mode 100644 bin/tests/system/serve-stale/ns5/named.conf.in create mode 100644 bin/tests/system/serve-stale/setup.sh create mode 100755 bin/tests/system/serve-stale/tests.sh create mode 100644 bin/tests/system/serve-stale/tests_sh_serve_stale.py create mode 100644 bin/tests/system/setup.sh create mode 100644 bin/tests/system/sfcache/README create mode 100644 bin/tests/system/sfcache/clean.sh create mode 100644 bin/tests/system/sfcache/ns1/named.conf.in create mode 100644 bin/tests/system/sfcache/ns1/root.db.in create mode 100644 bin/tests/system/sfcache/ns1/sign.sh create mode 100644 bin/tests/system/sfcache/ns2/example.db.in create mode 100644 bin/tests/system/sfcache/ns2/named.conf.in create mode 100644 bin/tests/system/sfcache/ns2/sign.sh create mode 100644 bin/tests/system/sfcache/ns5/named.conf.in create mode 100644 bin/tests/system/sfcache/ns5/sign.sh create mode 100644 bin/tests/system/sfcache/setup.sh create mode 100644 bin/tests/system/sfcache/tests.sh create mode 100644 bin/tests/system/sfcache/tests_sh_sfcache.py create mode 100644 bin/tests/system/shutdown/clean.sh create mode 100644 bin/tests/system/shutdown/ns1/named.conf.in create mode 100644 bin/tests/system/shutdown/ns1/root.db create mode 100644 bin/tests/system/shutdown/ns2/named.conf.in create mode 100644 bin/tests/system/shutdown/ns2/test.db create mode 100644 bin/tests/system/shutdown/resolver/named.conf.in create mode 100644 bin/tests/system/shutdown/resolver/root.db create mode 100644 bin/tests/system/shutdown/setup.sh create mode 100755 bin/tests/system/shutdown/tests_shutdown.py create mode 100644 bin/tests/system/smartsign/child.db create mode 100644 bin/tests/system/smartsign/clean.sh create mode 100644 bin/tests/system/smartsign/parent.db create mode 100644 bin/tests/system/smartsign/tests.sh create mode 100644 bin/tests/system/smartsign/tests_sh_smartsign.py create mode 100644 bin/tests/system/sortlist/clean.sh create mode 100644 bin/tests/system/sortlist/ns1/example.db create mode 100644 bin/tests/system/sortlist/ns1/named.conf.in create mode 100644 bin/tests/system/sortlist/ns1/root.db create mode 100644 bin/tests/system/sortlist/setup.sh create mode 100644 bin/tests/system/sortlist/tests.sh create mode 100644 bin/tests/system/sortlist/tests_sh_sortlist.py create mode 100644 bin/tests/system/spf/clean.sh create mode 100644 bin/tests/system/spf/ns1/named.conf.in create mode 100644 bin/tests/system/spf/ns1/spf.db create mode 100644 bin/tests/system/spf/setup.sh create mode 100644 bin/tests/system/spf/tests.sh create mode 100644 bin/tests/system/spf/tests_sh_spf.py create mode 100755 bin/tests/system/start.pl create mode 100644 bin/tests/system/start.sh.in create mode 100755 bin/tests/system/staticstub/clean.sh create mode 100644 bin/tests/system/staticstub/conf/bad01.conf create mode 100644 bin/tests/system/staticstub/conf/bad02.conf create mode 100644 bin/tests/system/staticstub/conf/bad03.conf create mode 100644 bin/tests/system/staticstub/conf/bad04.conf create mode 100644 bin/tests/system/staticstub/conf/bad05.conf create mode 100644 bin/tests/system/staticstub/conf/bad06.conf create mode 100644 bin/tests/system/staticstub/conf/bad07.conf create mode 100644 bin/tests/system/staticstub/conf/bad08.conf create mode 100644 bin/tests/system/staticstub/conf/bad09.conf create mode 100644 bin/tests/system/staticstub/conf/bad10.conf create mode 100644 bin/tests/system/staticstub/conf/bad11.conf create mode 100644 bin/tests/system/staticstub/conf/good01.conf create mode 100644 bin/tests/system/staticstub/conf/good02.conf create mode 100644 bin/tests/system/staticstub/conf/good03.conf create mode 100644 bin/tests/system/staticstub/conf/good04.conf create mode 100644 bin/tests/system/staticstub/conf/good05.conf create mode 100644 bin/tests/system/staticstub/knowngood.dig.out.rec create mode 100644 bin/tests/system/staticstub/ns1/named.conf.in create mode 100644 bin/tests/system/staticstub/ns1/root.db create mode 100644 bin/tests/system/staticstub/ns2/named.conf.in create mode 100644 bin/tests/system/staticstub/ns3/example.db.in create mode 100644 bin/tests/system/staticstub/ns3/example.org.db create mode 100644 bin/tests/system/staticstub/ns3/named.conf.in create mode 100755 bin/tests/system/staticstub/ns3/sign.sh create mode 100644 bin/tests/system/staticstub/ns3/undelegated.db.in create mode 100644 bin/tests/system/staticstub/ns4/example.com.db create mode 100644 bin/tests/system/staticstub/ns4/example.info.db create mode 100644 bin/tests/system/staticstub/ns4/example.org.db create mode 100644 bin/tests/system/staticstub/ns4/named.conf.in create mode 100755 bin/tests/system/staticstub/ns4/sign.sh create mode 100644 bin/tests/system/staticstub/ns4/sub.example.db.in create mode 100755 bin/tests/system/staticstub/setup.sh create mode 100755 bin/tests/system/staticstub/tests.sh create mode 100644 bin/tests/system/staticstub/tests_sh_staticstub.py create mode 100644 bin/tests/system/statistics/ans4/ans.pl create mode 100644 bin/tests/system/statistics/clean.sh create mode 100644 bin/tests/system/statistics/ns1/named.conf.in create mode 100644 bin/tests/system/statistics/ns1/root.db create mode 100644 bin/tests/system/statistics/ns1/zone.db create mode 100644 bin/tests/system/statistics/ns2/example.db create mode 100644 bin/tests/system/statistics/ns2/internal.db create mode 100644 bin/tests/system/statistics/ns2/named.conf.in create mode 100644 bin/tests/system/statistics/ns2/named2.conf.in create mode 100644 bin/tests/system/statistics/ns3/internal.db create mode 100644 bin/tests/system/statistics/ns3/named.conf.in create mode 100644 bin/tests/system/statistics/ns3/root.hint create mode 100644 bin/tests/system/statistics/setup.sh create mode 100644 bin/tests/system/statistics/tests.sh create mode 100644 bin/tests/system/statistics/tests_sh_statistics.py create mode 100644 bin/tests/system/statschannel/clean.sh create mode 100644 bin/tests/system/statschannel/conftest.py create mode 100644 bin/tests/system/statschannel/fetch.pl create mode 100644 bin/tests/system/statschannel/generic.py create mode 100644 bin/tests/system/statschannel/generic_dnspython.py create mode 100644 bin/tests/system/statschannel/mem-xml.pl create mode 100644 bin/tests/system/statschannel/ns1/example.db create mode 100644 bin/tests/system/statschannel/ns1/named.conf.in create mode 100644 bin/tests/system/statschannel/ns2/dnssec.db.in create mode 100644 bin/tests/system/statschannel/ns2/example.db create mode 100644 bin/tests/system/statschannel/ns2/manykeys.db.in create mode 100644 bin/tests/system/statschannel/ns2/named.conf.in create mode 100644 bin/tests/system/statschannel/ns2/named2.conf.in create mode 100644 bin/tests/system/statschannel/ns2/sign.sh create mode 100644 bin/tests/system/statschannel/ns3/named.conf.in create mode 100644 bin/tests/system/statschannel/server-json.pl create mode 100644 bin/tests/system/statschannel/server-xml.pl create mode 100644 bin/tests/system/statschannel/setup.sh create mode 100644 bin/tests/system/statschannel/tests.sh create mode 100755 bin/tests/system/statschannel/tests_json.py create mode 100644 bin/tests/system/statschannel/tests_sh_statschannel.py create mode 100755 bin/tests/system/statschannel/tests_xml.py create mode 100644 bin/tests/system/statschannel/traffic-json.pl create mode 100644 bin/tests/system/statschannel/traffic-xml.pl create mode 100644 bin/tests/system/statschannel/traffic.expect.1 create mode 100644 bin/tests/system/statschannel/traffic.expect.2 create mode 100644 bin/tests/system/statschannel/traffic.expect.4 create mode 100644 bin/tests/system/statschannel/traffic.expect.5 create mode 100644 bin/tests/system/statschannel/traffic.expect.6 create mode 100644 bin/tests/system/statschannel/zones-json.pl create mode 100644 bin/tests/system/statschannel/zones-xml.pl create mode 100644 bin/tests/system/stop.pl create mode 100644 bin/tests/system/stop.sh.in create mode 100644 bin/tests/system/stopall.sh create mode 100644 bin/tests/system/stress/clean.sh create mode 100644 bin/tests/system/stress/ns2/named.conf.in create mode 100644 bin/tests/system/stress/ns2/zone.template.db create mode 100644 bin/tests/system/stress/ns3/named.conf.in create mode 100644 bin/tests/system/stress/ns4/named.conf.in create mode 100644 bin/tests/system/stress/setup.sh create mode 100644 bin/tests/system/stress/tests_stress_update.py create mode 100644 bin/tests/system/stub/clean.sh create mode 100644 bin/tests/system/stub/knowngood.dig.out.norec create mode 100644 bin/tests/system/stub/knowngood.dig.out.rec create mode 100644 bin/tests/system/stub/ns1/named.conf.in create mode 100644 bin/tests/system/stub/ns1/root.db create mode 100644 bin/tests/system/stub/ns2/child.example.db create mode 100644 bin/tests/system/stub/ns2/named.conf.in create mode 100644 bin/tests/system/stub/ns3/example.db create mode 100644 bin/tests/system/stub/ns3/named.conf.in create mode 100644 bin/tests/system/stub/ns4/example.db create mode 100644 bin/tests/system/stub/ns4/named.conf.in create mode 100644 bin/tests/system/stub/ns5/named.conf.in create mode 100644 bin/tests/system/stub/setup.sh create mode 100644 bin/tests/system/stub/tests.sh create mode 100644 bin/tests/system/stub/tests_sh_stub.py create mode 100644 bin/tests/system/synthfromdnssec/clean.sh create mode 100644 bin/tests/system/synthfromdnssec/ns1/dnamed.db.in create mode 100644 bin/tests/system/synthfromdnssec/ns1/example.db.in create mode 100644 bin/tests/system/synthfromdnssec/ns1/minimal.db.in create mode 100644 bin/tests/system/synthfromdnssec/ns1/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns1/root.db.in create mode 100644 bin/tests/system/synthfromdnssec/ns1/sign.sh create mode 100644 bin/tests/system/synthfromdnssec/ns1/soa-without-dnskey.db.in create mode 100644 bin/tests/system/synthfromdnssec/ns2/example.internal.db create mode 100644 bin/tests/system/synthfromdnssec/ns2/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns2/root.hints create mode 100644 bin/tests/system/synthfromdnssec/ns3/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns3/redirect.db create mode 100644 bin/tests/system/synthfromdnssec/ns3/root.hints create mode 100644 bin/tests/system/synthfromdnssec/ns4/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns4/root.hints create mode 100644 bin/tests/system/synthfromdnssec/ns5/internal2.db create mode 100644 bin/tests/system/synthfromdnssec/ns5/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns5/root.hints create mode 100644 bin/tests/system/synthfromdnssec/ns6/named.conf.in create mode 100644 bin/tests/system/synthfromdnssec/ns6/root.hints create mode 100644 bin/tests/system/synthfromdnssec/setup.sh create mode 100644 bin/tests/system/synthfromdnssec/tests.sh create mode 100644 bin/tests/system/synthfromdnssec/tests_sh_synthfromdnssec.py create mode 100644 bin/tests/system/tcp/1996-alloc_dnsbuf-crash-test.pkt create mode 100644 bin/tests/system/tcp/ans6/ans.py create mode 100644 bin/tests/system/tcp/clean.sh create mode 100644 bin/tests/system/tcp/ns1/named.conf.in create mode 100644 bin/tests/system/tcp/ns1/root.db create mode 100644 bin/tests/system/tcp/ns2/example.db create mode 100644 bin/tests/system/tcp/ns2/named.conf.in create mode 100644 bin/tests/system/tcp/ns3/named.conf.in create mode 100644 bin/tests/system/tcp/ns4/named.conf.in create mode 100644 bin/tests/system/tcp/ns5/named.conf.in create mode 100644 bin/tests/system/tcp/ns7/named.conf.in create mode 100644 bin/tests/system/tcp/ns7/named.dropedns create mode 100644 bin/tests/system/tcp/ns7/root.db create mode 100644 bin/tests/system/tcp/setup.sh create mode 100644 bin/tests/system/tcp/tests.sh create mode 100644 bin/tests/system/tcp/tests_sh_tcp.py create mode 100644 bin/tests/system/tcp/tests_tcp.py create mode 100755 bin/tests/system/testcrypto.sh create mode 100755 bin/tests/system/testsock.pl create mode 100644 bin/tests/system/testsock6.pl create mode 100644 bin/tests/system/testsummary.sh create mode 100644 bin/tests/system/timeouts/clean.sh create mode 100644 bin/tests/system/timeouts/ns1/example.db create mode 100644 bin/tests/system/timeouts/ns1/named.args create mode 100644 bin/tests/system/timeouts/ns1/named.conf.in create mode 100644 bin/tests/system/timeouts/ns1/root.db create mode 100644 bin/tests/system/timeouts/prereq.sh create mode 100644 bin/tests/system/timeouts/setup.sh create mode 100644 bin/tests/system/timeouts/tests_tcp_timeouts.py create mode 100644 bin/tests/system/tkey/clean.sh create mode 100644 bin/tests/system/tkey/keycreate.c create mode 100644 bin/tests/system/tkey/keydelete.c create mode 100644 bin/tests/system/tkey/ns1/example.db create mode 100644 bin/tests/system/tkey/ns1/named.conf.in create mode 100644 bin/tests/system/tkey/ns1/setup.sh create mode 100644 bin/tests/system/tkey/setup.sh create mode 100644 bin/tests/system/tkey/tests.sh create mode 100644 bin/tests/system/tkey/tests_sh_tkey.py create mode 100644 bin/tests/system/tools/clean.sh create mode 100644 bin/tests/system/tools/setup.sh create mode 100644 bin/tests/system/tools/tests.sh create mode 100644 bin/tests/system/tools/tests_sh_tools.py create mode 100644 bin/tests/system/transport-acl/clean.sh create mode 100644 bin/tests/system/transport-acl/ns1/named.conf.in create mode 100644 bin/tests/system/transport-acl/self-signed-cert.pem create mode 100644 bin/tests/system/transport-acl/self-signed-key.pem create mode 100644 bin/tests/system/transport-acl/setup.sh create mode 100644 bin/tests/system/transport-acl/tests.sh create mode 100644 bin/tests/system/transport-acl/tests_sh_transport_acl.py create mode 100644 bin/tests/system/tsig/ans2/ans.pl create mode 100644 bin/tests/system/tsig/badlocation create mode 100644 bin/tests/system/tsig/badtime create mode 100644 bin/tests/system/tsig/clean.sh create mode 100644 bin/tests/system/tsig/ns1/example.db create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-md5-legacy.+157+22023.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-md5-legacy.+157+22023.private create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha1-legacy.+161+50591.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha1-legacy.+161+50591.private create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha224-legacy.+162+50865.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha224-legacy.+162+50865.private create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha256-legacy.+163+38999.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha256-legacy.+163+38999.private create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha384-legacy.+164+56610.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha384-legacy.+164+56610.private create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha512-legacy.+165+22767.key create mode 100644 bin/tests/system/tsig/ns1/legacy/Khmac-sha512-legacy.+165+22767.private create mode 100644 bin/tests/system/tsig/ns1/named.conf.in create mode 100644 bin/tests/system/tsig/setup.sh create mode 100644 bin/tests/system/tsig/tests.sh create mode 100644 bin/tests/system/tsig/tests_sh_tsig.py create mode 100644 bin/tests/system/tsiggss/authsock.pl create mode 100644 bin/tests/system/tsiggss/clean.sh create mode 100644 bin/tests/system/tsiggss/ns1/administrator.ccache create mode 100644 bin/tests/system/tsiggss/ns1/dns.keytab create mode 100644 bin/tests/system/tsiggss/ns1/example.nil.db.in create mode 100644 bin/tests/system/tsiggss/ns1/named.conf.in create mode 100644 bin/tests/system/tsiggss/ns1/testdenied.ccache create mode 100644 bin/tests/system/tsiggss/prereq.sh create mode 100644 bin/tests/system/tsiggss/setup.sh create mode 100644 bin/tests/system/tsiggss/tests.sh create mode 100755 bin/tests/system/tsiggss/tests_isc_spnego_flaws.py create mode 100644 bin/tests/system/tsiggss/tests_sh_tsiggss.py create mode 100644 bin/tests/system/ttl/clean.sh create mode 100644 bin/tests/system/ttl/ns1/max-example.db create mode 100644 bin/tests/system/ttl/ns1/min-example.db create mode 100644 bin/tests/system/ttl/ns1/named.conf.in create mode 100644 bin/tests/system/ttl/ns2/hints.db create mode 100644 bin/tests/system/ttl/ns2/named.conf.in create mode 100644 bin/tests/system/ttl/setup.sh create mode 100644 bin/tests/system/ttl/tests_cache_ttl.py create mode 100644 bin/tests/system/unknown/clean.sh create mode 100644 bin/tests/system/unknown/large.out create mode 100644 bin/tests/system/unknown/ns1/broken1.db create mode 100644 bin/tests/system/unknown/ns1/broken2.db create mode 100644 bin/tests/system/unknown/ns1/broken3.db create mode 100644 bin/tests/system/unknown/ns1/broken4.db create mode 100644 bin/tests/system/unknown/ns1/broken5.db create mode 100644 bin/tests/system/unknown/ns1/class10.hints create mode 100644 bin/tests/system/unknown/ns1/example-class10.db create mode 100644 bin/tests/system/unknown/ns1/example-in.db create mode 100644 bin/tests/system/unknown/ns1/large.db create mode 100644 bin/tests/system/unknown/ns1/named.conf.in create mode 100644 bin/tests/system/unknown/ns2/named.conf.in create mode 100644 bin/tests/system/unknown/ns3/named.conf.in create mode 100644 bin/tests/system/unknown/ns3/sign.sh create mode 100644 bin/tests/system/unknown/setup.sh create mode 100644 bin/tests/system/unknown/tests.sh create mode 100644 bin/tests/system/unknown/tests_sh_unknown.py create mode 100644 bin/tests/system/unknown/zones/nan.bad create mode 100644 bin/tests/system/upforwd/ans4/ans.pl create mode 100644 bin/tests/system/upforwd/clean.sh create mode 100644 bin/tests/system/upforwd/knowngood.after1 create mode 100644 bin/tests/system/upforwd/knowngood.after2 create mode 100644 bin/tests/system/upforwd/knowngood.before create mode 100644 bin/tests/system/upforwd/knowngood.ns2.before create mode 100644 bin/tests/system/upforwd/ns1/example1.db create mode 100644 bin/tests/system/upforwd/ns1/named.conf.in create mode 100644 bin/tests/system/upforwd/ns2/named.conf.in create mode 100644 bin/tests/system/upforwd/ns3/named1.conf.in create mode 100644 bin/tests/system/upforwd/ns3/named2.conf.in create mode 100644 bin/tests/system/upforwd/ns3/noprimary.db create mode 100644 bin/tests/system/upforwd/setup.sh create mode 100644 bin/tests/system/upforwd/tests.sh create mode 100644 bin/tests/system/upforwd/tests_sh_upforwd.py create mode 100644 bin/tests/system/verify/clean.sh create mode 100644 bin/tests/system/verify/setup.sh create mode 100644 bin/tests/system/verify/tests.sh create mode 100644 bin/tests/system/verify/tests_sh_verify.py create mode 100644 bin/tests/system/verify/zones/genzones.sh create mode 100644 bin/tests/system/verify/zones/unsigned.db create mode 100644 bin/tests/system/views/clean.sh create mode 100644 bin/tests/system/views/ns1/named.conf.in create mode 100644 bin/tests/system/views/ns1/root.db create mode 100644 bin/tests/system/views/ns2/1.10.in-addr.arpa.db create mode 100644 bin/tests/system/views/ns2/clone.db create mode 100644 bin/tests/system/views/ns2/example1.db create mode 100644 bin/tests/system/views/ns2/example2.db create mode 100644 bin/tests/system/views/ns2/external/inline.db create mode 100644 bin/tests/system/views/ns2/internal.db create mode 100644 bin/tests/system/views/ns2/internal/inline.db create mode 100644 bin/tests/system/views/ns2/named1.conf.in create mode 100644 bin/tests/system/views/ns2/named2.conf.in create mode 100644 bin/tests/system/views/ns2/named3.conf.in create mode 100644 bin/tests/system/views/ns3/child.clone.db create mode 100644 bin/tests/system/views/ns3/internal.db create mode 100644 bin/tests/system/views/ns3/named1.conf.in create mode 100644 bin/tests/system/views/ns3/named2.conf.in create mode 100644 bin/tests/system/views/ns5/child.clone.db create mode 100644 bin/tests/system/views/ns5/named.conf.in create mode 100644 bin/tests/system/views/setup.sh create mode 100644 bin/tests/system/views/tests.sh create mode 100644 bin/tests/system/views/tests_sh_views.py create mode 100644 bin/tests/system/wildcard/clean.sh create mode 100644 bin/tests/system/wildcard/ns1/allwild.db.in create mode 100644 bin/tests/system/wildcard/ns1/dlv.db.in create mode 100644 bin/tests/system/wildcard/ns1/example.db.in create mode 100644 bin/tests/system/wildcard/ns1/named.conf.in create mode 100644 bin/tests/system/wildcard/ns1/nsec.db.in create mode 100644 bin/tests/system/wildcard/ns1/nsec3.db.in create mode 100644 bin/tests/system/wildcard/ns1/private.nsec.db.in create mode 100644 bin/tests/system/wildcard/ns1/private.nsec3.db.in create mode 100644 bin/tests/system/wildcard/ns1/root.db.in create mode 100755 bin/tests/system/wildcard/ns1/sign.sh create mode 100644 bin/tests/system/wildcard/ns2/named.conf.in create mode 100644 bin/tests/system/wildcard/ns3/named.conf.in create mode 100644 bin/tests/system/wildcard/ns4/named.conf.in create mode 100644 bin/tests/system/wildcard/ns5/named.conf.in create mode 100644 bin/tests/system/wildcard/setup.sh create mode 100644 bin/tests/system/wildcard/tests.sh create mode 100644 bin/tests/system/wildcard/tests_sh_wildcard.py create mode 100755 bin/tests/system/wildcard/tests_wildcard.py create mode 100644 bin/tests/system/xfer/ans5/badkeydata create mode 100644 bin/tests/system/xfer/ans5/badmessageid create mode 100644 bin/tests/system/xfer/ans5/goodaxfr create mode 100644 bin/tests/system/xfer/ans5/partial create mode 100644 bin/tests/system/xfer/ans5/soamismatch create mode 100644 bin/tests/system/xfer/ans5/unknownkey create mode 100644 bin/tests/system/xfer/ans5/unsigned create mode 100644 bin/tests/system/xfer/ans5/wrongkey create mode 100644 bin/tests/system/xfer/ans5/wrongname create mode 100644 bin/tests/system/xfer/axfr-stats.good create mode 100644 bin/tests/system/xfer/clean.sh create mode 100644 bin/tests/system/xfer/dig1.good create mode 100644 bin/tests/system/xfer/dig2.good create mode 100644 bin/tests/system/xfer/dig3.good create mode 100644 bin/tests/system/xfer/knowngood.mapped create mode 100644 bin/tests/system/xfer/ns1/axfr-max-idle-time.db create mode 100644 bin/tests/system/xfer/ns1/axfr-max-transfer-time.db create mode 100644 bin/tests/system/xfer/ns1/axfr-too-big.db create mode 100644 bin/tests/system/xfer/ns1/dot-fallback.db.in create mode 100644 bin/tests/system/xfer/ns1/ixfr-too-big.db.in create mode 100644 bin/tests/system/xfer/ns1/named1.conf.in create mode 100644 bin/tests/system/xfer/ns1/named2.conf.in create mode 100644 bin/tests/system/xfer/ns1/named3.conf.in create mode 100644 bin/tests/system/xfer/ns1/root.db create mode 100644 bin/tests/system/xfer/ns1/xfer-stats.db create mode 100644 bin/tests/system/xfer/ns2/mapped.db.in create mode 100644 bin/tests/system/xfer/ns2/named.conf.in create mode 100644 bin/tests/system/xfer/ns2/sec.db.in create mode 100644 bin/tests/system/xfer/ns3/named.conf.in create mode 100644 bin/tests/system/xfer/ns4/named.conf.base create mode 100644 bin/tests/system/xfer/ns4/root.db.in create mode 100644 bin/tests/system/xfer/ns6/named.args create mode 100644 bin/tests/system/xfer/ns6/named.conf.in create mode 100644 bin/tests/system/xfer/ns7/named.conf.in create mode 100644 bin/tests/system/xfer/ns8/example.db create mode 100644 bin/tests/system/xfer/ns8/named.conf.in create mode 100644 bin/tests/system/xfer/prereq.sh create mode 100644 bin/tests/system/xfer/setup.sh create mode 100755 bin/tests/system/xfer/tests.sh create mode 100644 bin/tests/system/xfer/tests_sh_xfer.py create mode 100644 bin/tests/system/xferquota/clean.sh create mode 100644 bin/tests/system/xferquota/ns1/changing1.db create mode 100644 bin/tests/system/xferquota/ns1/changing2.db create mode 100644 bin/tests/system/xferquota/ns1/named.conf.in create mode 100644 bin/tests/system/xferquota/ns1/root.db create mode 100644 bin/tests/system/xferquota/ns2/example.db create mode 100644 bin/tests/system/xferquota/ns2/named.conf.in create mode 100644 bin/tests/system/xferquota/setup.pl create mode 100644 bin/tests/system/xferquota/setup.sh create mode 100755 bin/tests/system/xferquota/tests.sh create mode 100644 bin/tests/system/xferquota/tests_sh_xferquota.py create mode 100644 bin/tests/system/zero/ans5/ans.pl create mode 100644 bin/tests/system/zero/clean.sh create mode 100644 bin/tests/system/zero/ns1/named.conf.in create mode 100644 bin/tests/system/zero/ns1/root.db create mode 100644 bin/tests/system/zero/ns2/named.args create mode 100644 bin/tests/system/zero/ns2/named.conf.in create mode 100644 bin/tests/system/zero/ns2/tld.db create mode 100644 bin/tests/system/zero/ns3/named.args create mode 100644 bin/tests/system/zero/ns3/named.conf.in create mode 100644 bin/tests/system/zero/ns3/root.hint create mode 100644 bin/tests/system/zero/ns4/named.args create mode 100644 bin/tests/system/zero/ns4/named.conf.in create mode 100644 bin/tests/system/zero/ns4/one.tld.db create mode 100644 bin/tests/system/zero/setup.sh create mode 100644 bin/tests/system/zero/tests.sh create mode 100644 bin/tests/system/zero/tests_sh_zero.py create mode 100644 bin/tests/system/zonechecks/a.db create mode 100644 bin/tests/system/zonechecks/aaaa.db create mode 100644 bin/tests/system/zonechecks/bigserial.db create mode 100644 bin/tests/system/zonechecks/clean.sh create mode 100644 bin/tests/system/zonechecks/cname.db create mode 100644 bin/tests/system/zonechecks/dname.db create mode 100644 bin/tests/system/zonechecks/noaddress.db create mode 100644 bin/tests/system/zonechecks/ns1/named.conf.in create mode 100644 bin/tests/system/zonechecks/ns2/named.conf.in create mode 100644 bin/tests/system/zonechecks/nxdomain.db create mode 100644 bin/tests/system/zonechecks/setup.sh create mode 100644 bin/tests/system/zonechecks/tests.sh create mode 100644 bin/tests/system/zonechecks/tests_sh_zonechecks.py create mode 100644 bin/tests/test_client.c create mode 100644 bin/tests/test_server.c create mode 100644 bin/tests/wire_test.c create mode 100644 bin/tools/Makefile.am create mode 100644 bin/tools/Makefile.in create mode 100644 bin/tools/arpaname.c create mode 100644 bin/tools/arpaname.rst create mode 100644 bin/tools/dnstap-read.c create mode 100644 bin/tools/dnstap-read.rst create mode 100644 bin/tools/mdig.c create mode 100644 bin/tools/mdig.rst create mode 100644 bin/tools/named-journalprint.c create mode 100644 bin/tools/named-journalprint.rst create mode 100644 bin/tools/named-nzd2nzf.c create mode 100644 bin/tools/named-nzd2nzf.rst create mode 100644 bin/tools/named-rrchecker.c create mode 100644 bin/tools/named-rrchecker.rst create mode 100644 bin/tools/nsec3hash.c create mode 100644 bin/tools/nsec3hash.rst (limited to 'bin') diff --git a/bin/Makefile.am b/bin/Makefile.am new file mode 100644 index 0000000..ba7658e --- /dev/null +++ b/bin/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = named rndc dig delv dnssec tools nsupdate check confgen tests plugins diff --git a/bin/Makefile.in b/bin/Makefile.in new file mode 100644 index 0000000..d2bb917 --- /dev/null +++ b/bin/Makefile.in @@ -0,0 +1,734 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = bin +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = named rndc dig delv dnssec tools nsupdate check confgen tests plugins +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +doc: doc-recursive + +doc-am: doc-local + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +test: test-recursive + +test-am: test-local + +uninstall-am: + +unit: unit-recursive + +unit-am: unit-local + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am test-am \ + test-local uninstall uninstall-am unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/check/Makefile.am b/bin/check/Makefile.am new file mode 100644 index 0000000..8f63c35 --- /dev/null +++ b/bin/check/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBBIND9_CFLAGS) + +AM_CPPFLAGS += \ + -DNAMED_CONFFILE=\"${sysconfdir}/named.conf\" + +noinst_LTLIBRARIES = libcheck-tool.la + +libcheck_tool_la_SOURCES = \ + check-tool.h \ + check-tool.c + +LDADD += \ + libcheck-tool.la \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) + +bin_PROGRAMS = named-checkconf named-checkzone + +install-exec-hook: + ln -f $(DESTDIR)$(bindir)/named-checkzone \ + $(DESTDIR)$(bindir)/named-compilezone + +uninstall-hook: + -rm -f $(DESTDIR)$(bindir)/named-compilezone diff --git a/bin/check/Makefile.in b/bin/check/Makefile.in new file mode 100644 index 0000000..1f2d67a --- /dev/null +++ b/bin/check/Makefile.in @@ -0,0 +1,872 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +bin_PROGRAMS = named-checkconf$(EXEEXT) named-checkzone$(EXEEXT) +subdir = bin/check +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libcheck_tool_la_LIBADD = +am_libcheck_tool_la_OBJECTS = check-tool.lo +libcheck_tool_la_OBJECTS = $(am_libcheck_tool_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +named_checkconf_SOURCES = named-checkconf.c +named_checkconf_OBJECTS = named-checkconf.$(OBJEXT) +named_checkconf_LDADD = $(LDADD) +named_checkconf_DEPENDENCIES = libcheck-tool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) $(LIBNS_LIBS) $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) +named_checkzone_SOURCES = named-checkzone.c +named_checkzone_OBJECTS = named-checkzone.$(OBJEXT) +named_checkzone_LDADD = $(LDADD) +named_checkzone_DEPENDENCIES = libcheck-tool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) $(LIBNS_LIBS) $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/check-tool.Plo \ + ./$(DEPDIR)/named-checkconf.Po ./$(DEPDIR)/named-checkzone.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcheck_tool_la_SOURCES) named-checkconf.c \ + named-checkzone.c +DIST_SOURCES = $(libcheck_tool_la_SOURCES) named-checkconf.c \ + named-checkzone.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) $(LIBISCCFG_CFLAGS) $(LIBBIND9_CFLAGS) \ + -DNAMED_CONFFILE=\"${sysconfdir}/named.conf\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = libcheck-tool.la $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBNS_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +noinst_LTLIBRARIES = libcheck-tool.la +libcheck_tool_la_SOURCES = \ + check-tool.h \ + check-tool.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/check/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/check/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libcheck-tool.la: $(libcheck_tool_la_OBJECTS) $(libcheck_tool_la_DEPENDENCIES) $(EXTRA_libcheck_tool_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcheck_tool_la_OBJECTS) $(libcheck_tool_la_LIBADD) $(LIBS) + +named-checkconf$(EXEEXT): $(named_checkconf_OBJECTS) $(named_checkconf_DEPENDENCIES) $(EXTRA_named_checkconf_DEPENDENCIES) + @rm -f named-checkconf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(named_checkconf_OBJECTS) $(named_checkconf_LDADD) $(LIBS) + +named-checkzone$(EXEEXT): $(named_checkzone_OBJECTS) $(named_checkzone_DEPENDENCIES) $(EXTRA_named_checkzone_DEPENDENCIES) + @rm -f named-checkzone$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(named_checkzone_OBJECTS) $(named_checkzone_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check-tool.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/named-checkconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/named-checkzone.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/check-tool.Plo + -rm -f ./$(DEPDIR)/named-checkconf.Po + -rm -f ./$(DEPDIR)/named-checkzone.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/check-tool.Plo + -rm -f ./$(DEPDIR)/named-checkconf.Po + -rm -f ./$(DEPDIR)/named-checkzone.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-binPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-exec-am install-strip uninstall-am + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-exec-hook install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-hook unit-am \ + unit-local + +.PRECIOUS: Makefile + + +install-exec-hook: + ln -f $(DESTDIR)$(bindir)/named-checkzone \ + $(DESTDIR)$(bindir)/named-compilezone + +uninstall-hook: + -rm -f $(DESTDIR)$(bindir)/named-compilezone + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/check/check-tool.c b/bin/check/check-tool.c new file mode 100644 index 0000000..fd008dc --- /dev/null +++ b/bin/check/check-tool.c @@ -0,0 +1,693 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "check-tool.h" + +#ifndef CHECK_SIBLING +#define CHECK_SIBLING 1 +#endif /* ifndef CHECK_SIBLING */ + +#ifndef CHECK_LOCAL +#define CHECK_LOCAL 1 +#endif /* ifndef CHECK_LOCAL */ + +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +#define ERR_IS_CNAME 1 +#define ERR_NO_ADDRESSES 2 +#define ERR_LOOKUP_FAILURE 3 +#define ERR_EXTRA_A 4 +#define ERR_EXTRA_AAAA 5 +#define ERR_MISSING_GLUE 5 +#define ERR_IS_MXCNAME 6 +#define ERR_IS_SRVCNAME 7 + +static const char *dbtype[] = { "rbt" }; + +int debug = 0; +const char *journal = NULL; +bool nomerge = true; +#if CHECK_LOCAL +bool docheckmx = true; +bool dochecksrv = true; +bool docheckns = true; +#else /* if CHECK_LOCAL */ +bool docheckmx = false; +bool dochecksrv = false; +bool docheckns = false; +#endif /* if CHECK_LOCAL */ +dns_zoneopt_t zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_MANYERRORS | DNS_ZONEOPT_CHECKNAMES | + DNS_ZONEOPT_CHECKINTEGRITY | +#if CHECK_SIBLING + DNS_ZONEOPT_CHECKSIBLING | +#endif /* if CHECK_SIBLING */ + DNS_ZONEOPT_CHECKWILDCARD | + DNS_ZONEOPT_WARNMXCNAME | DNS_ZONEOPT_WARNSRVCNAME; + +/* + * This needs to match the list in bin/named/log.c. + */ +static isc_logcategory_t categories[] = { { "", 0 }, + { "unmatched", 0 }, + { NULL, 0 } }; + +static isc_symtab_t *symtab = NULL; +static isc_mem_t *sym_mctx; + +static void +freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { + UNUSED(type); + UNUSED(value); + isc_mem_free(userarg, key); +} + +static void +add(char *key, int value) { + isc_result_t result; + isc_symvalue_t symvalue; + + if (sym_mctx == NULL) { + isc_mem_create(&sym_mctx); + } + + if (symtab == NULL) { + result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx, + false, &symtab); + if (result != ISC_R_SUCCESS) { + return; + } + } + + key = isc_mem_strdup(sym_mctx, key); + + symvalue.as_pointer = NULL; + result = isc_symtab_define(symtab, key, value, symvalue, + isc_symexists_reject); + if (result != ISC_R_SUCCESS) { + isc_mem_free(sym_mctx, key); + } +} + +static bool +logged(char *key, int value) { + isc_result_t result; + + if (symtab == NULL) { + return (false); + } + + result = isc_symtab_lookup(symtab, key, value, NULL); + if (result == ISC_R_SUCCESS) { + return (true); + } + return (false); +} + +static bool +checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner, + dns_rdataset_t *a, dns_rdataset_t *aaaa) { + dns_rdataset_t *rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + struct addrinfo hints, *ai, *cur; + char namebuf[DNS_NAME_FORMATSIZE + 1]; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")]; + bool answer = true; + bool match; + const char *type; + void *ptr = NULL; + int result; + + REQUIRE(a == NULL || !dns_rdataset_isassociated(a) || + a->type == dns_rdatatype_a); + REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) || + aaaa->type == dns_rdatatype_aaaa); + + if (a == NULL || aaaa == NULL) { + return (answer); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + /* + * Turn off search. + */ + if (dns_name_countlabels(name) > 1U) { + strlcat(namebuf, ".", sizeof(namebuf)); + } + dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); + + result = getaddrinfo(namebuf, NULL, &hints, &ai); + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + switch (result) { + case 0: + /* + * Work around broken getaddrinfo() implementations that + * fail to set ai_canonname on first entry. + */ + cur = ai; + while (cur != NULL && cur->ai_canonname == NULL && + cur->ai_next != NULL) + { + cur = cur->ai_next; + } + if (cur != NULL && cur->ai_canonname != NULL && + strcasecmp(cur->ai_canonname, namebuf) != 0 && + !logged(namebuf, ERR_IS_CNAME)) + { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/NS '%s' (out of zone) " + "is a CNAME '%s' (illegal)", + ownerbuf, namebuf, cur->ai_canonname); + /* XXX950 make fatal for 9.5.0 */ + /* answer = false; */ + add(namebuf, ERR_IS_CNAME); + } + break; + case EAI_NONAME: +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ + if (!logged(namebuf, ERR_NO_ADDRESSES)) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/NS '%s' (out of zone) " + "has no addresses records (A or AAAA)", + ownerbuf, namebuf); + add(namebuf, ERR_NO_ADDRESSES); + } + /* XXX950 make fatal for 9.5.0 */ + return (true); + + default: + if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { + dns_zone_log(zone, ISC_LOG_WARNING, + "getaddrinfo(%s) failed: %s", namebuf, + gai_strerror(result)); + add(namebuf, ERR_LOOKUP_FAILURE); + } + return (true); + } + + /* + * Check that all glue records really exist. + */ + if (!dns_rdataset_isassociated(a)) { + goto checkaaaa; + } + result = dns_rdataset_first(a); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(a, &rdata); + match = false; + for (cur = ai; cur != NULL; cur = cur->ai_next) { + if (cur->ai_family != AF_INET) { + continue; + } + ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr; + if (memcmp(ptr, rdata.data, rdata.length) == 0) { + match = true; + break; + } + } + if (!match && !logged(namebuf, ERR_EXTRA_A)) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/NS '%s' " + "extra GLUE A record (%s)", + ownerbuf, namebuf, + inet_ntop(AF_INET, rdata.data, addrbuf, + sizeof(addrbuf))); + add(namebuf, ERR_EXTRA_A); + /* XXX950 make fatal for 9.5.0 */ + /* answer = false; */ + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(a); + } + +checkaaaa: + if (!dns_rdataset_isassociated(aaaa)) { + goto checkmissing; + } + result = dns_rdataset_first(aaaa); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(aaaa, &rdata); + match = false; + for (cur = ai; cur != NULL; cur = cur->ai_next) { + if (cur->ai_family != AF_INET6) { + continue; + } + ptr = &((struct sockaddr_in6 *)(cur->ai_addr)) + ->sin6_addr; + if (memcmp(ptr, rdata.data, rdata.length) == 0) { + match = true; + break; + } + } + if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/NS '%s' " + "extra GLUE AAAA record (%s)", + ownerbuf, namebuf, + inet_ntop(AF_INET6, rdata.data, addrbuf, + sizeof(addrbuf))); + add(namebuf, ERR_EXTRA_AAAA); + /* XXX950 make fatal for 9.5.0. */ + /* answer = false; */ + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(aaaa); + } + +checkmissing: + /* + * Check that all addresses appear in the glue. + */ + if (!logged(namebuf, ERR_MISSING_GLUE)) { + bool missing_glue = false; + for (cur = ai; cur != NULL; cur = cur->ai_next) { + switch (cur->ai_family) { + case AF_INET: + rdataset = a; + ptr = &((struct sockaddr_in *)(cur->ai_addr)) + ->sin_addr; + type = "A"; + break; + case AF_INET6: + rdataset = aaaa; + ptr = &((struct sockaddr_in6 *)(cur->ai_addr)) + ->sin6_addr; + type = "AAAA"; + break; + default: + continue; + } + match = false; + if (dns_rdataset_isassociated(rdataset)) { + result = dns_rdataset_first(rdataset); + } else { + result = ISC_R_FAILURE; + } + while (result == ISC_R_SUCCESS && !match) { + dns_rdataset_current(rdataset, &rdata); + if (memcmp(ptr, rdata.data, rdata.length) == 0) + { + match = true; + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(rdataset); + } + if (!match) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/NS '%s' " + "missing GLUE %s record (%s)", + ownerbuf, namebuf, type, + inet_ntop(cur->ai_family, ptr, + addrbuf, + sizeof(addrbuf))); + /* XXX950 make fatal for 9.5.0. */ + /* answer = false; */ + missing_glue = true; + } + } + if (missing_glue) { + add(namebuf, ERR_MISSING_GLUE); + } + } + freeaddrinfo(ai); + return (answer); +} + +static bool +checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) { + struct addrinfo hints, *ai, *cur; + char namebuf[DNS_NAME_FORMATSIZE + 1]; + char ownerbuf[DNS_NAME_FORMATSIZE]; + int result; + int level = ISC_LOG_ERROR; + bool answer = true; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + /* + * Turn off search. + */ + if (dns_name_countlabels(name) > 1U) { + strlcat(namebuf, ".", sizeof(namebuf)); + } + dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); + + result = getaddrinfo(namebuf, NULL, &hints, &ai); + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + switch (result) { + case 0: + /* + * Work around broken getaddrinfo() implementations that + * fail to set ai_canonname on first entry. + */ + cur = ai; + while (cur != NULL && cur->ai_canonname == NULL && + cur->ai_next != NULL) + { + cur = cur->ai_next; + } + if (cur != NULL && cur->ai_canonname != NULL && + strcasecmp(cur->ai_canonname, namebuf) != 0) + { + if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0) { + level = ISC_LOG_WARNING; + } + if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) { + if (!logged(namebuf, ERR_IS_MXCNAME)) { + dns_zone_log(zone, level, + "%s/MX '%s' (out of zone)" + " is a CNAME '%s' " + "(illegal)", + ownerbuf, namebuf, + cur->ai_canonname); + add(namebuf, ERR_IS_MXCNAME); + } + if (level == ISC_LOG_ERROR) { + answer = false; + } + } + } + freeaddrinfo(ai); + return (answer); + + case EAI_NONAME: +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ + if (!logged(namebuf, ERR_NO_ADDRESSES)) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/MX '%s' (out of zone) " + "has no addresses records (A or AAAA)", + ownerbuf, namebuf); + add(namebuf, ERR_NO_ADDRESSES); + } + /* XXX950 make fatal for 9.5.0. */ + return (true); + + default: + if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { + dns_zone_log(zone, ISC_LOG_WARNING, + "getaddrinfo(%s) failed: %s", namebuf, + gai_strerror(result)); + add(namebuf, ERR_LOOKUP_FAILURE); + } + return (true); + } +} + +static bool +checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) { + struct addrinfo hints, *ai, *cur; + char namebuf[DNS_NAME_FORMATSIZE + 1]; + char ownerbuf[DNS_NAME_FORMATSIZE]; + int result; + int level = ISC_LOG_ERROR; + bool answer = true; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + /* + * Turn off search. + */ + if (dns_name_countlabels(name) > 1U) { + strlcat(namebuf, ".", sizeof(namebuf)); + } + dns_name_format(owner, ownerbuf, sizeof(ownerbuf)); + + result = getaddrinfo(namebuf, NULL, &hints, &ai); + dns_name_format(name, namebuf, sizeof(namebuf) - 1); + switch (result) { + case 0: + /* + * Work around broken getaddrinfo() implementations that + * fail to set ai_canonname on first entry. + */ + cur = ai; + while (cur != NULL && cur->ai_canonname == NULL && + cur->ai_next != NULL) + { + cur = cur->ai_next; + } + if (cur != NULL && cur->ai_canonname != NULL && + strcasecmp(cur->ai_canonname, namebuf) != 0) + { + if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0) { + level = ISC_LOG_WARNING; + } + if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) { + if (!logged(namebuf, ERR_IS_SRVCNAME)) { + dns_zone_log(zone, level, + "%s/SRV '%s'" + " (out of zone) is a " + "CNAME '%s' (illegal)", + ownerbuf, namebuf, + cur->ai_canonname); + add(namebuf, ERR_IS_SRVCNAME); + } + if (level == ISC_LOG_ERROR) { + answer = false; + } + } + } + freeaddrinfo(ai); + return (answer); + + case EAI_NONAME: +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */ + if (!logged(namebuf, ERR_NO_ADDRESSES)) { + dns_zone_log(zone, ISC_LOG_ERROR, + "%s/SRV '%s' (out of zone) " + "has no addresses records (A or AAAA)", + ownerbuf, namebuf); + add(namebuf, ERR_NO_ADDRESSES); + } + /* XXX950 make fatal for 9.5.0. */ + return (true); + + default: + if (!logged(namebuf, ERR_LOOKUP_FAILURE)) { + dns_zone_log(zone, ISC_LOG_WARNING, + "getaddrinfo(%s) failed: %s", namebuf, + gai_strerror(result)); + add(namebuf, ERR_LOOKUP_FAILURE); + } + return (true); + } +} + +isc_result_t +setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) { + isc_logdestination_t destination; + isc_logconfig_t *logconfig = NULL; + isc_log_t *log = NULL; + + isc_log_create(mctx, &log, &logconfig); + isc_log_registercategories(log, categories); + isc_log_setcontext(log); + dns_log_init(log); + dns_log_setcontext(log); + cfg_log_init(log); + ns_log_init(log); + + destination.file.stream = errout; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, + ISC_LOG_DYNAMIC, &destination, 0); + + RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) == + ISC_R_SUCCESS); + + *logp = log; + return (ISC_R_SUCCESS); +} + +/*% load the zone */ +isc_result_t +load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, + dns_masterformat_t fileformat, const char *classname, + dns_ttl_t maxttl, dns_zone_t **zonep) { + isc_result_t result; + dns_rdataclass_t rdclass; + isc_textregion_t region; + isc_buffer_t buffer; + dns_fixedname_t fixorigin; + dns_name_t *origin; + dns_zone_t *zone = NULL; + + REQUIRE(zonep == NULL || *zonep == NULL); + + if (debug) { + fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n", + zonename, filename, classname); + } + + CHECK(dns_zone_create(&zone, mctx)); + + dns_zone_settype(zone, dns_zone_primary); + + isc_buffer_constinit(&buffer, zonename, strlen(zonename)); + isc_buffer_add(&buffer, strlen(zonename)); + origin = dns_fixedname_initname(&fixorigin); + CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL)); + CHECK(dns_zone_setorigin(zone, origin)); + dns_zone_setdbtype(zone, 1, (const char *const *)dbtype); + if (strcmp(filename, "-") == 0) { + CHECK(dns_zone_setstream(zone, stdin, fileformat, + &dns_master_style_default)); + } else { + CHECK(dns_zone_setfile(zone, filename, fileformat, + &dns_master_style_default)); + } + if (journal != NULL) { + CHECK(dns_zone_setjournal(zone, journal)); + } + + DE_CONST(classname, region.base); + region.length = strlen(classname); + CHECK(dns_rdataclass_fromtext(&rdclass, ®ion)); + + dns_zone_setclass(zone, rdclass); + dns_zone_setoption(zone, zone_options, true); + dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge); + + dns_zone_setmaxttl(zone, maxttl); + + if (docheckmx) { + dns_zone_setcheckmx(zone, checkmx); + } + if (docheckns) { + dns_zone_setcheckns(zone, checkns); + } + if (dochecksrv) { + dns_zone_setchecksrv(zone, checksrv); + } + + CHECK(dns_zone_load(zone, false)); + + if (zonep != NULL) { + *zonep = zone; + zone = NULL; + } + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + return (result); +} + +/*% dump the zone */ +isc_result_t +dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, + dns_masterformat_t fileformat, const dns_master_style_t *style, + const uint32_t rawversion) { + isc_result_t result; + FILE *output = stdout; + const char *flags; + + flags = (fileformat == dns_masterformat_text) ? "w" : "wb"; + + if (debug) { + if (filename != NULL && strcmp(filename, "-") != 0) { + fprintf(stderr, "dumping \"%s\" to \"%s\"\n", zonename, + filename); + } else { + fprintf(stderr, "dumping \"%s\"\n", zonename); + } + } + + if (filename != NULL && strcmp(filename, "-") != 0) { + result = isc_stdio_open(filename, flags, &output); + + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "could not open output " + "file \"%s\" for writing\n", + filename); + return (ISC_R_FAILURE); + } + } + + result = dns_zone_dumptostream(zone, output, fileformat, style, + rawversion); + if (output != stdout) { + (void)isc_stdio_close(output); + } + + return (result); +} diff --git a/bin/check/check-tool.h b/bin/check/check-tool.h new file mode 100644 index 0000000..64ac9e7 --- /dev/null +++ b/bin/check/check-tool.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp); + +isc_result_t +load_zone(isc_mem_t *mctx, const char *zonename, const char *filename, + dns_masterformat_t fileformat, const char *classname, + dns_ttl_t maxttl, dns_zone_t **zonep); + +isc_result_t +dump_zone(const char *zonename, dns_zone_t *zone, const char *filename, + dns_masterformat_t fileformat, const dns_master_style_t *style, + const uint32_t rawversion); + +extern int debug; +extern const char *journal; +extern bool nomerge; +extern bool docheckmx; +extern bool docheckns; +extern bool dochecksrv; +extern dns_zoneopt_t zone_options; + +ISC_LANG_ENDDECLS diff --git a/bin/check/named-checkconf.c b/bin/check/named-checkconf.c new file mode 100644 index 0000000..fd7cfa5 --- /dev/null +++ b/bin/check/named-checkconf.c @@ -0,0 +1,754 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "check-tool.h" + +static const char *program = "named-checkconf"; + +static bool loadplugins = true; + +isc_log_t *logc = NULL; + +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +/*% usage */ +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, + "usage: %s [-chijlvz] [-p [-x]] [-t directory] " + "[named.conf]\n", + program); + exit(1); +} + +/*% directory callback */ +static isc_result_t +directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) { + isc_result_t result; + const char *directory; + + REQUIRE(strcasecmp("directory", clausename) == 0); + + UNUSED(arg); + UNUSED(clausename); + + /* + * Change directory. + */ + directory = cfg_obj_asstring(obj); + result = isc_dir_chdir(directory); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, logc, ISC_LOG_ERROR, + "change directory to '%s' failed: %s\n", directory, + isc_result_totext(result)); + return (result); + } + + return (ISC_R_SUCCESS); +} + +static bool +get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) { + int i; + for (i = 0;; i++) { + if (maps[i] == NULL) { + return (false); + } + if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) { + return (true); + } + } +} + +static bool +get_checknames(const cfg_obj_t **maps, const cfg_obj_t **obj) { + const cfg_listelt_t *element; + const cfg_obj_t *checknames; + const cfg_obj_t *type; + const cfg_obj_t *value; + isc_result_t result; + int i; + + for (i = 0;; i++) { + if (maps[i] == NULL) { + return (false); + } + checknames = NULL; + result = cfg_map_get(maps[i], "check-names", &checknames); + if (result != ISC_R_SUCCESS) { + continue; + } + if (checknames != NULL && !cfg_obj_islist(checknames)) { + *obj = checknames; + return (true); + } + for (element = cfg_list_first(checknames); element != NULL; + element = cfg_list_next(element)) + { + value = cfg_listelt_value(element); + type = cfg_tuple_get(value, "type"); + if ((strcasecmp(cfg_obj_asstring(type), "primary") != + 0) && + (strcasecmp(cfg_obj_asstring(type), "master") != 0)) + { + continue; + } + *obj = cfg_tuple_get(value, "mode"); + return (true); + } + } +} + +static isc_result_t +configure_hint(const char *zfile, const char *zclass, isc_mem_t *mctx) { + isc_result_t result; + dns_db_t *db = NULL; + dns_rdataclass_t rdclass; + isc_textregion_t r; + + if (zfile == NULL) { + return (ISC_R_FAILURE); + } + + DE_CONST(zclass, r.base); + r.length = strlen(zclass); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = dns_rootns_create(mctx, rdclass, zfile, &db); + if (result != ISC_R_SUCCESS) { + return (result); + } + + dns_db_detach(&db); + return (ISC_R_SUCCESS); +} + +/*% configure the zone */ +static isc_result_t +configure_zone(const char *vclass, const char *view, const cfg_obj_t *zconfig, + const cfg_obj_t *vconfig, const cfg_obj_t *config, + isc_mem_t *mctx, bool list) { + int i = 0; + isc_result_t result; + const char *zclass; + const char *zname; + const char *zfile = NULL; + const cfg_obj_t *maps[4]; + const cfg_obj_t *primariesobj = NULL; + const cfg_obj_t *inviewobj = NULL; + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *classobj = NULL; + const cfg_obj_t *typeobj = NULL; + const cfg_obj_t *fileobj = NULL; + const cfg_obj_t *dlzobj = NULL; + const cfg_obj_t *dbobj = NULL; + const cfg_obj_t *obj = NULL; + const cfg_obj_t *fmtobj = NULL; + dns_masterformat_t masterformat; + dns_ttl_t maxttl = 0; + + zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS; + + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + classobj = cfg_tuple_get(zconfig, "class"); + if (!cfg_obj_isstring(classobj)) { + zclass = vclass; + } else { + zclass = cfg_obj_asstring(classobj); + } + + zoptions = cfg_tuple_get(zconfig, "options"); + maps[i++] = zoptions; + if (vconfig != NULL) { + maps[i++] = cfg_tuple_get(vconfig, "options"); + } + if (config != NULL) { + cfg_map_get(config, "options", &obj); + if (obj != NULL) { + maps[i++] = obj; + } + } + maps[i] = NULL; + + cfg_map_get(zoptions, "in-view", &inviewobj); + if (inviewobj != NULL && list) { + const char *inview = cfg_obj_asstring(inviewobj); + printf("%s %s %s in-view %s\n", zname, zclass, view, inview); + } + if (inviewobj != NULL) { + return (ISC_R_SUCCESS); + } + + cfg_map_get(zoptions, "type", &typeobj); + if (typeobj == NULL) { + return (ISC_R_FAILURE); + } + + if (list) { + const char *ztype = cfg_obj_asstring(typeobj); + printf("%s %s %s %s\n", zname, zclass, view, ztype); + return (ISC_R_SUCCESS); + } + + /* + * Skip checks when using an alternate data source. + */ + cfg_map_get(zoptions, "database", &dbobj); + if (dbobj != NULL && strcmp("rbt", cfg_obj_asstring(dbobj)) != 0 && + strcmp("rbt64", cfg_obj_asstring(dbobj)) != 0) + { + return (ISC_R_SUCCESS); + } + + cfg_map_get(zoptions, "dlz", &dlzobj); + if (dlzobj != NULL) { + return (ISC_R_SUCCESS); + } + + cfg_map_get(zoptions, "file", &fileobj); + if (fileobj != NULL) { + zfile = cfg_obj_asstring(fileobj); + } + + /* + * Check hints files for hint zones. + * Skip loading checks for any type other than + * master and redirect + */ + if (strcasecmp(cfg_obj_asstring(typeobj), "hint") == 0) { + return (configure_hint(zfile, zclass, mctx)); + } else if ((strcasecmp(cfg_obj_asstring(typeobj), "primary") != 0) && + (strcasecmp(cfg_obj_asstring(typeobj), "master") != 0) && + (strcasecmp(cfg_obj_asstring(typeobj), "redirect") != 0)) + { + return (ISC_R_SUCCESS); + } + + /* + * Is the redirect zone configured as a secondary? + */ + if (strcasecmp(cfg_obj_asstring(typeobj), "redirect") == 0) { + cfg_map_get(zoptions, "primaries", &primariesobj); + if (primariesobj == NULL) { + cfg_map_get(zoptions, "masters", &primariesobj); + } + + if (primariesobj != NULL) { + return (ISC_R_SUCCESS); + } + } + + if (zfile == NULL) { + return (ISC_R_FAILURE); + } + + obj = NULL; + if (get_maps(maps, "check-dup-records", &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_CHECKDUPRR; + zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + zone_options |= DNS_ZONEOPT_CHECKDUPRR; + zone_options |= DNS_ZONEOPT_CHECKDUPRRFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options &= ~DNS_ZONEOPT_CHECKDUPRR; + zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_CHECKDUPRR; + zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; + } + + obj = NULL; + if (get_maps(maps, "check-mx", &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_CHECKMX; + zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + zone_options |= DNS_ZONEOPT_CHECKMX; + zone_options |= DNS_ZONEOPT_CHECKMXFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options &= ~DNS_ZONEOPT_CHECKMX; + zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_CHECKMX; + zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; + } + + obj = NULL; + if (get_maps(maps, "check-integrity", &obj)) { + if (cfg_obj_asboolean(obj)) { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; + } else { + zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY; + } + } else { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; + } + + obj = NULL; + if (get_maps(maps, "check-mx-cname", &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_WARNMXCNAME; + zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + zone_options &= ~DNS_ZONEOPT_WARNMXCNAME; + zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options |= DNS_ZONEOPT_WARNMXCNAME; + zone_options |= DNS_ZONEOPT_IGNOREMXCNAME; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_WARNMXCNAME; + zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; + } + + obj = NULL; + if (get_maps(maps, "check-srv-cname", &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_WARNSRVCNAME; + zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME; + zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options |= DNS_ZONEOPT_WARNSRVCNAME; + zone_options |= DNS_ZONEOPT_IGNORESRVCNAME; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_WARNSRVCNAME; + zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; + } + + obj = NULL; + if (get_maps(maps, "check-sibling", &obj)) { + if (cfg_obj_asboolean(obj)) { + zone_options |= DNS_ZONEOPT_CHECKSIBLING; + } else { + zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; + } + } + + obj = NULL; + if (get_maps(maps, "check-spf", &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_CHECKSPF; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options &= ~DNS_ZONEOPT_CHECKSPF; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_CHECKSPF; + } + + obj = NULL; + if (get_maps(maps, "check-wildcard", &obj)) { + if (cfg_obj_asboolean(obj)) { + zone_options |= DNS_ZONEOPT_CHECKWILDCARD; + } else { + zone_options &= ~DNS_ZONEOPT_CHECKWILDCARD; + } + } else { + zone_options |= DNS_ZONEOPT_CHECKWILDCARD; + } + + obj = NULL; + if (get_checknames(maps, &obj)) { + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + zone_options |= DNS_ZONEOPT_CHECKNAMES; + zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + zone_options |= DNS_ZONEOPT_CHECKNAMES; + zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + zone_options &= ~DNS_ZONEOPT_CHECKNAMES; + zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; + } else { + UNREACHABLE(); + } + } else { + zone_options |= DNS_ZONEOPT_CHECKNAMES; + zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL; + } + + masterformat = dns_masterformat_text; + fmtobj = NULL; + if (get_maps(maps, "masterfile-format", &fmtobj)) { + const char *masterformatstr = cfg_obj_asstring(fmtobj); + if (strcasecmp(masterformatstr, "text") == 0) { + masterformat = dns_masterformat_text; + } else if (strcasecmp(masterformatstr, "raw") == 0) { + masterformat = dns_masterformat_raw; + } else { + UNREACHABLE(); + } + } + + obj = NULL; + if (get_maps(maps, "max-zone-ttl", &obj)) { + maxttl = cfg_obj_asduration(obj); + zone_options |= DNS_ZONEOPT_CHECKTTL; + } + + result = load_zone(mctx, zname, zfile, masterformat, zclass, maxttl, + NULL); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass, + isc_result_totext(result)); + } + return (result); +} + +/*% configure a view */ +static isc_result_t +configure_view(const char *vclass, const char *view, const cfg_obj_t *config, + const cfg_obj_t *vconfig, isc_mem_t *mctx, bool list) { + const cfg_listelt_t *element; + const cfg_obj_t *voptions; + const cfg_obj_t *zonelist; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + + voptions = NULL; + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + } + + zonelist = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "zone", &zonelist); + } else { + (void)cfg_map_get(config, "zone", &zonelist); + } + + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig = cfg_listelt_value(element); + tresult = configure_zone(vclass, view, zconfig, vconfig, config, + mctx, list); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + return (result); +} + +static isc_result_t +config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass, + dns_rdataclass_t *classp) { + isc_textregion_t r; + + if (!cfg_obj_isstring(classobj)) { + *classp = defclass; + return (ISC_R_SUCCESS); + } + DE_CONST(cfg_obj_asstring(classobj), r.base); + r.length = strlen(r.base); + return (dns_rdataclass_fromtext(classp, &r)); +} + +/*% load zones from the configuration */ +static isc_result_t +load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx, + bool list_zones) { + const cfg_listelt_t *element; + const cfg_obj_t *views; + const cfg_obj_t *vconfig; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + + views = NULL; + + (void)cfg_map_get(config, "view", &views); + for (element = cfg_list_first(views); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *classobj; + dns_rdataclass_t viewclass; + const char *vname; + char buf[sizeof("CLASS65535")]; + + vconfig = cfg_listelt_value(element); + if (vconfig == NULL) { + continue; + } + + classobj = cfg_tuple_get(vconfig, "class"); + tresult = config_getclass(classobj, dns_rdataclass_in, + &viewclass); + if (tresult != ISC_R_SUCCESS) { + CHECK(tresult); + } + + if (dns_rdataclass_ismeta(viewclass)) { + CHECK(ISC_R_FAILURE); + } + + dns_rdataclass_format(viewclass, buf, sizeof(buf)); + vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); + tresult = configure_view(buf, vname, config, vconfig, mctx, + list_zones); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + + if (views == NULL) { + tresult = configure_view("IN", "_default", config, NULL, mctx, + list_zones); + if (tresult != ISC_R_SUCCESS) { + result = tresult; + } + } + +cleanup: + return (result); +} + +static void +output(void *closure, const char *text, int textlen) { + UNUSED(closure); + if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) { + perror("fwrite"); + exit(1); + } +} + +/*% The main processing routine */ +int +main(int argc, char **argv) { + int c; + cfg_parser_t *parser = NULL; + cfg_obj_t *config = NULL; + const char *conffile = NULL; + isc_mem_t *mctx = NULL; + isc_result_t result; + int exit_status = 0; + bool load_zones = false; + bool list_zones = false; + bool print = false; + bool nodeprecate = false; + unsigned int flags = 0; + + isc_commandline_errprint = false; + + /* + * Process memory debugging argument first. + */ +#define CMDLINE_FLAGS "cdhijlm:t:pvxz" + while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (c) { + case 'm': + if (strcasecmp(isc_commandline_argument, "record") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } + if (strcasecmp(isc_commandline_argument, "trace") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } + if (strcasecmp(isc_commandline_argument, "usage") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + default: + break; + } + } + isc_commandline_reset = true; + + isc_mem_create(&mctx); + + while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != EOF) { + switch (c) { + case 'c': + loadplugins = false; + break; + + case 'd': + debug++; + break; + + case 'i': + nodeprecate = true; + break; + + case 'j': + nomerge = false; + break; + + case 'l': + list_zones = true; + break; + + case 'm': + break; + + case 't': + result = isc_dir_chroot(isc_commandline_argument); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "isc_dir_chroot: %s\n", + isc_result_totext(result)); + exit(1); + } + break; + + case 'p': + print = true; + break; + + case 'v': + printf("%s\n", PACKAGE_VERSION); + exit(0); + + case 'x': + flags |= CFG_PRINTER_XKEY; + break; + + case 'z': + load_zones = true; + docheckmx = false; + docheckns = false; + dochecksrv = false; + break; + + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + usage(); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + if (((flags & CFG_PRINTER_XKEY) != 0) && !print) { + fprintf(stderr, "%s: -x cannot be used without -p\n", program); + exit(1); + } + if (print && list_zones) { + fprintf(stderr, "%s: -l cannot be used with -p\n", program); + exit(1); + } + + if (isc_commandline_index + 1 < argc) { + usage(); + } + if (argv[isc_commandline_index] != NULL) { + conffile = argv[isc_commandline_index]; + } + if (conffile == NULL || conffile[0] == '\0') { + conffile = NAMED_CONFFILE; + } + + RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS); + + RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS); + + if (nodeprecate) { + cfg_parser_setflags(parser, CFG_PCTX_NODEPRECATED, true); + } + cfg_parser_setcallback(parser, directory_callback, NULL); + + if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) != + ISC_R_SUCCESS) + { + exit(1); + } + + result = bind9_check_namedconf(config, loadplugins, nodeprecate, logc, + mctx); + if (result != ISC_R_SUCCESS) { + exit_status = 1; + } + + if (result == ISC_R_SUCCESS && (load_zones || list_zones)) { + result = load_zones_fromconfig(config, mctx, list_zones); + if (result != ISC_R_SUCCESS) { + exit_status = 1; + } + } + + if (print && exit_status == 0) { + cfg_printx(config, flags, output, NULL); + } + cfg_obj_destroy(parser, &config); + + cfg_parser_destroy(&parser); + + isc_log_destroy(&logc); + + isc_mem_destroy(&mctx); + + return (exit_status); +} diff --git a/bin/check/named-checkconf.rst b/bin/check/named-checkconf.rst new file mode 100644 index 0000000..b3065a5 --- /dev/null +++ b/bin/check/named-checkconf.rst @@ -0,0 +1,108 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: named-checkconf +.. program:: named-checkconf +.. _man_named-checkconf: + +named-checkconf - named configuration file syntax checking tool +--------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`named-checkconf` [**-chjlvz**] [**-p** [**-x** ]] [**-t** directory] {filename} + +Description +~~~~~~~~~~~ + +:program:`named-checkconf` checks the syntax, but not the semantics, of a +:iscman:`named` configuration file. The file, along with all files included by it, is parsed and checked for syntax +errors. If no file is specified, +|named_conf| is read by default. + +Note: files that :iscman:`named` reads in separate parser contexts, such as +``rndc.key`` and ``bind.keys``, are not automatically read by +:program:`named-checkconf`. Configuration errors in these files may cause +:iscman:`named` to fail to run, even if :program:`named-checkconf` was successful. +However, :program:`named-checkconf` can be run on these files explicitly. + +Options +~~~~~~~ + +.. option:: -h + + This option prints the usage summary and exits. + +.. option:: -j + + When loading a zonefile, this option instructs :iscman:`named` to read the journal if it exists. + +.. option:: -l + + This option lists all the configured zones. Each line of output contains the zone + name, class (e.g. IN), view, and type (e.g. primary or secondary). + +.. option:: -c + + This option specifies that only the "core" configuration should be checked. This suppresses the loading of + plugin modules, and causes all parameters to ``plugin`` statements to + be ignored. + +.. option:: -i + + This option ignores warnings on deprecated options. + +.. option:: -p + + This option prints out the :iscman:`named.conf` and included files in canonical form if + no errors were detected. See also the :option:`-x` option. + +.. option:: -t directory + + This option instructs :iscman:`named` to chroot to ``directory``, so that ``include`` directives in the + configuration file are processed as if run by a similarly chrooted + :iscman:`named`. + +.. option:: -v + + This option prints the version of the :program:`named-checkconf` program and exits. + +.. option:: -x + + When printing the configuration files in canonical form, this option obscures + shared secrets by replacing them with strings of question marks + (``?``). This allows the contents of :iscman:`named.conf` and related files + to be shared - for example, when submitting bug reports - + without compromising private data. This option cannot be used without + :option:`-p`. + +.. option:: -z + + This option performs a test load of all zones of type ``primary`` found in :iscman:`named.conf`. + +.. option:: filename + + This indicates the name of the configuration file to be checked. If not specified, + it defaults to |named_conf|. + +Return Values +~~~~~~~~~~~~~ + +:program:`named-checkconf` returns an exit status of 1 if errors were detected +and 0 otherwise. + +See Also +~~~~~~~~ + +:iscman:`named(8) `, :iscman:`named-checkzone(8) `, BIND 9 Administrator Reference Manual. diff --git a/bin/check/named-checkzone.c b/bin/check/named-checkzone.c new file mode 100644 index 0000000..5c74660 --- /dev/null +++ b/bin/check/named-checkzone.c @@ -0,0 +1,569 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "check-tool.h" + +static int quiet = 0; +static isc_mem_t *mctx = NULL; +dns_zone_t *zone = NULL; +dns_zonetype_t zonetype = dns_zone_primary; +static int dumpzone = 0; +static const char *output_filename; +static const char *prog_name = NULL; +static const dns_master_style_t *outputstyle = NULL; +static enum { progmode_check, progmode_compile } progmode; + +#define ERRRET(result, function) \ + do { \ + if (result != ISC_R_SUCCESS) { \ + if (!quiet) \ + fprintf(stderr, "%s() returned %s\n", \ + function, isc_result_totext(result)); \ + return (result); \ + } \ + } while (0) + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, + "usage: %s [-djqvD] [-c class] " + "[-f inputformat] [-F outputformat] [-J filename] " + "[-s (full|relative)] [-t directory] [-w directory] " + "[-k (ignore|warn|fail)] [-m (ignore|warn|fail)] " + "[-n (ignore|warn|fail)] [-r (ignore|warn|fail)] " + "[-i (full|full-sibling|local|local-sibling|none)] " + "[-M (ignore|warn|fail)] [-S (ignore|warn|fail)] " + "[-W (ignore|warn)] " + "%s zonename [ (filename|-) ]\n", + prog_name, + progmode == progmode_check ? "[-o filename]" : "-o filename"); + exit(1); +} + +static void +destroy(void) { + if (zone != NULL) { + dns_zone_detach(&zone); + } +} + +/*% main processing routine */ +int +main(int argc, char **argv) { + int c; + char *origin = NULL; + const char *filename = NULL; + isc_log_t *lctx = NULL; + isc_result_t result; + char classname_in[] = "IN"; + char *classname = classname_in; + const char *workdir = NULL; + const char *inputformatstr = NULL; + const char *outputformatstr = NULL; + dns_masterformat_t inputformat = dns_masterformat_text; + dns_masterformat_t outputformat = dns_masterformat_text; + dns_masterrawheader_t header; + uint32_t rawversion = 1, serialnum = 0; + dns_ttl_t maxttl = 0; + bool snset = false; + bool logdump = false; + FILE *errout = stdout; + char *endp; + + /* + * Uncomment the following line if memory debugging is needed: + * isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + */ + + outputstyle = &dns_master_style_full; + + prog_name = strrchr(argv[0], '/'); + if (prog_name == NULL) { + prog_name = strrchr(argv[0], '\\'); + } + if (prog_name != NULL) { + prog_name++; + } else { + prog_name = argv[0]; + } + /* + * Libtool doesn't preserve the program name prior to final + * installation. Remove the libtool prefix ("lt-"). + */ + if (strncmp(prog_name, "lt-", 3) == 0) { + prog_name += 3; + } + +#define PROGCMP(X) \ + (strcasecmp(prog_name, X) == 0 || strcasecmp(prog_name, X ".exe") == 0) + + if (PROGCMP("named-checkzone")) { + progmode = progmode_check; + } else if (PROGCMP("named-compilezone")) { + progmode = progmode_compile; + } else { + UNREACHABLE(); + } + + /* Compilation specific defaults */ + if (progmode == progmode_compile) { + zone_options |= (DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_FATALNS | + DNS_ZONEOPT_CHECKSPF | DNS_ZONEOPT_CHECKDUPRR | + DNS_ZONEOPT_CHECKNAMES | + DNS_ZONEOPT_CHECKNAMESFAIL | + DNS_ZONEOPT_CHECKWILDCARD); + } else { + zone_options |= (DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF); + } + +#define ARGCMP(X) (strcmp(isc_commandline_argument, X) == 0) + + isc_commandline_errprint = false; + + while ((c = isc_commandline_parse(argc, argv, + "c:df:hi:jJ:k:L:l:m:n:qr:s:t:o:vw:DF:" + "M:S:T:W:")) != EOF) + { + switch (c) { + case 'c': + classname = isc_commandline_argument; + break; + + case 'd': + debug++; + break; + + case 'i': + if (ARGCMP("full")) { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY | + DNS_ZONEOPT_CHECKSIBLING; + docheckmx = true; + docheckns = true; + dochecksrv = true; + } else if (ARGCMP("full-sibling")) { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; + zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; + docheckmx = true; + docheckns = true; + dochecksrv = true; + } else if (ARGCMP("local")) { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; + zone_options |= DNS_ZONEOPT_CHECKSIBLING; + docheckmx = false; + docheckns = false; + dochecksrv = false; + } else if (ARGCMP("local-sibling")) { + zone_options |= DNS_ZONEOPT_CHECKINTEGRITY; + zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; + docheckmx = false; + docheckns = false; + dochecksrv = false; + } else if (ARGCMP("none")) { + zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY; + zone_options &= ~DNS_ZONEOPT_CHECKSIBLING; + docheckmx = false; + docheckns = false; + dochecksrv = false; + } else { + fprintf(stderr, "invalid argument to -i: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'f': + inputformatstr = isc_commandline_argument; + break; + + case 'F': + outputformatstr = isc_commandline_argument; + break; + + case 'j': + nomerge = false; + break; + + case 'J': + journal = isc_commandline_argument; + nomerge = false; + break; + + case 'k': + if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKNAMES; + zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL; + } else if (ARGCMP("fail")) { + zone_options |= DNS_ZONEOPT_CHECKNAMES | + DNS_ZONEOPT_CHECKNAMESFAIL; + } else if (ARGCMP("ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKNAMES | + DNS_ZONEOPT_CHECKNAMESFAIL); + } else { + fprintf(stderr, "invalid argument to -k: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'L': + snset = true; + endp = NULL; + serialnum = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "source serial number " + "must be numeric"); + exit(1); + } + break; + + case 'l': + zone_options |= DNS_ZONEOPT_CHECKTTL; + endp = NULL; + maxttl = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "maximum TTL " + "must be numeric"); + exit(1); + } + break; + + case 'n': + if (ARGCMP("ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKNS | + DNS_ZONEOPT_FATALNS); + } else if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKNS; + zone_options &= ~DNS_ZONEOPT_FATALNS; + } else if (ARGCMP("fail")) { + zone_options |= DNS_ZONEOPT_CHECKNS | + DNS_ZONEOPT_FATALNS; + } else { + fprintf(stderr, "invalid argument to -n: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'm': + if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKMX; + zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; + } else if (ARGCMP("fail")) { + zone_options |= DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_CHECKMXFAIL; + } else if (ARGCMP("ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_CHECKMXFAIL); + } else { + fprintf(stderr, "invalid argument to -m: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'o': + output_filename = isc_commandline_argument; + break; + + case 'q': + quiet++; + break; + + case 'r': + if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKDUPRR; + zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL; + } else if (ARGCMP("fail")) { + zone_options |= DNS_ZONEOPT_CHECKDUPRR | + DNS_ZONEOPT_CHECKDUPRRFAIL; + } else if (ARGCMP("ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKDUPRR | + DNS_ZONEOPT_CHECKDUPRRFAIL); + } else { + fprintf(stderr, "invalid argument to -r: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 's': + if (ARGCMP("full")) { + outputstyle = &dns_master_style_full; + } else if (ARGCMP("relative")) { + outputstyle = &dns_master_style_default; + } else { + fprintf(stderr, + "unknown or unsupported style: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 't': + result = isc_dir_chroot(isc_commandline_argument); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "isc_dir_chroot: %s: %s\n", + isc_commandline_argument, + isc_result_totext(result)); + exit(1); + } + break; + + case 'v': + printf("%s\n", PACKAGE_VERSION); + exit(0); + + case 'w': + workdir = isc_commandline_argument; + break; + + case 'D': + dumpzone++; + break; + + case 'M': + if (ARGCMP("fail")) { + zone_options &= ~DNS_ZONEOPT_WARNMXCNAME; + zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; + } else if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_WARNMXCNAME; + zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME; + } else if (ARGCMP("ignore")) { + zone_options |= DNS_ZONEOPT_WARNMXCNAME; + zone_options |= DNS_ZONEOPT_IGNOREMXCNAME; + } else { + fprintf(stderr, "invalid argument to -M: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'S': + if (ARGCMP("fail")) { + zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME; + zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; + } else if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_WARNSRVCNAME; + zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME; + } else if (ARGCMP("ignore")) { + zone_options |= DNS_ZONEOPT_WARNSRVCNAME; + zone_options |= DNS_ZONEOPT_IGNORESRVCNAME; + } else { + fprintf(stderr, "invalid argument to -S: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'T': + if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKSPF; + } else if (ARGCMP("ignore")) { + zone_options &= ~DNS_ZONEOPT_CHECKSPF; + } else { + fprintf(stderr, "invalid argument to -T: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'W': + if (ARGCMP("warn")) { + zone_options |= DNS_ZONEOPT_CHECKWILDCARD; + } else if (ARGCMP("ignore")) { + zone_options &= ~DNS_ZONEOPT_CHECKWILDCARD; + } + break; + + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + prog_name, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + usage(); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", prog_name, + isc_commandline_option); + exit(1); + } + } + + if (workdir != NULL) { + result = isc_dir_chdir(workdir); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "isc_dir_chdir: %s: %s\n", workdir, + isc_result_totext(result)); + exit(1); + } + } + + if (inputformatstr != NULL) { + if (strcasecmp(inputformatstr, "text") == 0) { + inputformat = dns_masterformat_text; + } else if (strcasecmp(inputformatstr, "raw") == 0) { + inputformat = dns_masterformat_raw; + } else if (strncasecmp(inputformatstr, "raw=", 4) == 0) { + inputformat = dns_masterformat_raw; + fprintf(stderr, "WARNING: input format raw, version " + "ignored\n"); + } else { + fprintf(stderr, "unknown file format: %s\n", + inputformatstr); + exit(1); + } + } + + if (outputformatstr != NULL) { + if (strcasecmp(outputformatstr, "text") == 0) { + outputformat = dns_masterformat_text; + } else if (strcasecmp(outputformatstr, "raw") == 0) { + outputformat = dns_masterformat_raw; + } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) { + char *end; + + outputformat = dns_masterformat_raw; + rawversion = strtol(outputformatstr + 4, &end, 10); + if (end == outputformatstr + 4 || *end != '\0' || + rawversion > 1U) + { + fprintf(stderr, "unknown raw format version\n"); + exit(1); + } + } else { + fprintf(stderr, "unknown file format: %s\n", + outputformatstr); + exit(1); + } + } + + if (progmode == progmode_compile) { + dumpzone = 1; /* always dump */ + logdump = !quiet; + if (output_filename == NULL) { + fprintf(stderr, "output file required, but not " + "specified\n"); + usage(); + } + } + + if (output_filename != NULL) { + dumpzone = 1; + } + + /* + * If we are printing to stdout then send the informational + * output to stderr. + */ + if (dumpzone && + (output_filename == NULL || strcmp(output_filename, "-") == 0 || + strcmp(output_filename, "/dev/fd/1") == 0 || + strcmp(output_filename, "/dev/stdout") == 0)) + { + errout = stderr; + logdump = false; + } + + if (argc - isc_commandline_index < 1 || + argc - isc_commandline_index > 2) + { + usage(); + } + + isc_mem_create(&mctx); + if (!quiet) { + RUNTIME_CHECK(setup_logging(mctx, errout, &lctx) == + ISC_R_SUCCESS); + } + + origin = argv[isc_commandline_index++]; + + if (isc_commandline_index == argc) { + /* "-" will be interpreted as stdin */ + filename = "-"; + } else { + filename = argv[isc_commandline_index]; + } + + isc_commandline_index++; + + result = load_zone(mctx, origin, filename, inputformat, classname, + maxttl, &zone); + + if (snset) { + dns_master_initrawheader(&header); + header.flags = DNS_MASTERRAW_SOURCESERIALSET; + header.sourceserial = serialnum; + dns_zone_setrawdata(zone, &header); + } + + if (result == ISC_R_SUCCESS && dumpzone) { + if (logdump) { + fprintf(errout, "dump zone to %s...", output_filename); + fflush(errout); + } + result = dump_zone(origin, zone, output_filename, outputformat, + outputstyle, rawversion); + if (logdump) { + fprintf(errout, "done\n"); + } + } + + if (!quiet && result == ISC_R_SUCCESS) { + fprintf(errout, "OK\n"); + } + destroy(); + if (lctx != NULL) { + isc_log_destroy(&lctx); + } + isc_mem_destroy(&mctx); + + return ((result == ISC_R_SUCCESS) ? 0 : 1); +} diff --git a/bin/check/named-checkzone.rst b/bin/check/named-checkzone.rst new file mode 100644 index 0000000..563752f --- /dev/null +++ b/bin/check/named-checkzone.rst @@ -0,0 +1,222 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. BEWARE: Do not forget to edit also named-compilezone.rst! + +.. iscman:: named-checkzone +.. program:: named-checkzone +.. _man_named-checkzone: + +named-checkzone - zone file validation tool +------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`named-checkzone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-o** filename] [**-r** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {zonename} {filename} + +Description +~~~~~~~~~~~ + +:program:`named-checkzone` checks the syntax and integrity of a zone file. It +performs the same checks as :iscman:`named` does when loading a zone. This +makes :program:`named-checkzone` useful for checking zone files before +configuring them into a name server. + +Options +~~~~~~~ + +.. option:: -d + + This option enables debugging. + +.. option:: -h + + This option prints the usage summary and exits. + +.. option:: -q + + This option sets quiet mode, which only sets an exit code to indicate + successful or failed completion. + +.. option:: -v + + This option prints the version of the :program:`named-checkzone` program and exits. + +.. option:: -j + + When loading a zone file, this option tells :iscman:`named` to read the journal if it exists. The journal + file name is assumed to be the zone file name with the + string ``.jnl`` appended. + +.. option:: -J filename + + When loading the zone file, this option tells :iscman:`named` to read the journal from the given file, if + it exists. This implies :option:`-j`. + +.. option:: -c class + + This option specifies the class of the zone. If not specified, ``IN`` is assumed. + +.. option:: -i mode + + This option performs post-load zone integrity checks. Possible modes are + ``full`` (the default), ``full-sibling``, ``local``, + ``local-sibling``, and ``none``. + + Mode ``full`` checks that MX records refer to A or AAAA records + (both in-zone and out-of-zone hostnames). Mode ``local`` only + checks MX records which refer to in-zone hostnames. + + Mode ``full`` checks that SRV records refer to A or AAAA records + (both in-zone and out-of-zone hostnames). Mode ``local`` only + checks SRV records which refer to in-zone hostnames. + + Mode ``full`` checks that delegation NS records refer to A or AAAA + records (both in-zone and out-of-zone hostnames). It also checks that + glue address records in the zone match those advertised by the child. + Mode ``local`` only checks NS records which refer to in-zone + hostnames or verifies that some required glue exists, i.e., when the + name server is in a child zone. + + Modes ``full-sibling`` and ``local-sibling`` disable sibling glue + checks, but are otherwise the same as ``full`` and ``local``, + respectively. + + Mode ``none`` disables the checks. + +.. option:: -f format + + This option specifies the format of the zone file. Possible formats are + ``text`` (the default), and ``raw``. + +.. option:: -F format + + This option specifies the format of the output file specified. For + :program:`named-checkzone`, this does not have any effect unless it dumps + the zone contents. + + Possible formats are ``text`` (the default), which is the standard + textual representation of the zone, and ``raw`` and ``raw=N``, which + store the zone in a binary format for rapid loading by :iscman:`named`. + ``raw=N`` specifies the format version of the raw zone file: if ``N`` is + 0, the raw file can be read by any version of :iscman:`named`; if N is 1, the + file can only be read by release 9.9.0 or higher. The default is 1. + +.. option:: -k mode + + This option performs ``check-names`` checks with the specified failure mode. + Possible modes are ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -l ttl + + This option sets a maximum permissible TTL for the input file. Any record with a + TTL higher than this value causes the zone to be rejected. This + is similar to using the ``max-zone-ttl`` option in :iscman:`named.conf`. + +.. option:: -L serial + + When compiling a zone to ``raw`` format, this option sets the "source + serial" value in the header to the specified serial number. This is + expected to be used primarily for testing purposes. + +.. option:: -m mode + + This option specifies whether MX records should be checked to see if they are + addresses. Possible modes are ``fail``, ``warn`` (the default), and + ``ignore``. + +.. option:: -M mode + + This option checks whether a MX record refers to a CNAME. Possible modes are + ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -n mode + + This option specifies whether NS records should be checked to see if they are + addresses. Possible modes are ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -o filename + + This option writes the zone output to ``filename``. If ``filename`` is ``-``, then + the zone output is written to standard output. + +.. option:: -r mode + + This option checks for records that are treated as different by DNSSEC but are + semantically equal in plain DNS. Possible modes are ``fail``, + ``warn`` (the default), and ``ignore``. + +.. option:: -s style + + This option specifies the style of the dumped zone file. Possible styles are + ``full`` (the default) and ``relative``. The ``full`` format is most + suitable for processing automatically by a separate script. + The relative format is more human-readable and is thus + suitable for editing by hand. This does not have any effect unless it dumps + the zone contents. It also does not have any meaning if the output format + is not text. + +.. option:: -S mode + + This option checks whether an SRV record refers to a CNAME. Possible modes are + ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -t directory + + This option tells :iscman:`named` to chroot to ``directory``, so that ``include`` directives in the + configuration file are processed as if run by a similarly chrooted + :iscman:`named`. + +.. option:: -T mode + + This option checks whether Sender Policy Framework (SPF) records exist and issues a + warning if an SPF-formatted TXT record is not also present. Possible + modes are ``warn`` (the default) and ``ignore``. + +.. option:: -w directory + + This option instructs :iscman:`named` to chdir to ``directory``, so that relative filenames in master file + ``$INCLUDE`` directives work. This is similar to the directory clause in + :iscman:`named.conf`. + +.. option:: -D + + This option dumps the zone file in canonical format. + +.. option:: -W mode + + This option specifies whether to check for non-terminal wildcards. Non-terminal + wildcards are almost always the result of a failure to understand the + wildcard matching algorithm (:rfc:`4592`). Possible modes are ``warn`` + (the default) and ``ignore``. + +.. option:: zonename + + This indicates the domain name of the zone being checked. + +.. option:: filename + + This is the name of the zone file. + +Return Values +~~~~~~~~~~~~~ + +:program:`named-checkzone` returns an exit status of 1 if errors were detected +and 0 otherwise. + +See Also +~~~~~~~~ + +:iscman:`named(8) `, :iscman:`named-checkconf(8) `, :iscman:`named-compilezone(8) `, :rfc:`1035`, BIND 9 Administrator Reference +Manual. diff --git a/bin/check/named-compilezone.rst b/bin/check/named-compilezone.rst new file mode 100644 index 0000000..41ecff7 --- /dev/null +++ b/bin/check/named-compilezone.rst @@ -0,0 +1,224 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. BEWARE: Do not forget to edit also named-checkzone.rst! + +.. iscman:: named-compilezone +.. program:: named-compilezone +.. _man_named-compilezone: + +named-compilezone - zone file converting tool +--------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`named-compilezone` [**-d**] [**-h**] [**-j**] [**-q**] [**-v**] [**-c** class] [**-f** format] [**-F** format] [**-J** filename] [**-i** mode] [**-k** mode] [**-m** mode] [**-M** mode] [**-n** mode] [**-l** ttl] [**-L** serial] [**-r** mode] [**-s** style] [**-S** mode] [**-t** directory] [**-T** mode] [**-w** directory] [**-D**] [**-W** mode] {**-o** filename} {zonename} {filename} + +Description +~~~~~~~~~~~ + +:program:`named-compilezone` checks the syntax and integrity of a zone file, +and dumps the zone contents to a specified file in a specified format. +It applies strict check levels by default, since the +dump output is used as an actual zone file loaded by :iscman:`named`. +When manually specified otherwise, the check levels must at least be as +strict as those specified in the :iscman:`named` configuration file. + +Options +~~~~~~~ + +.. option:: -d + + This option enables debugging. + +.. option:: -h + + This option prints the usage summary and exits. + +.. option:: -q + + This option sets quiet mode, which only sets an exit code to indicate + successful or failed completion. + +.. option:: -v + + This option prints the version of the :iscman:`named-checkzone` program and exits. + +.. option:: -j + + When loading a zone file, this option tells :iscman:`named` to read the journal if it exists. The journal + file name is assumed to be the zone file name with the + string ``.jnl`` appended. + +.. option:: -J filename + + When loading the zone file, this option tells :iscman:`named` to read the journal from the given file, if + it exists. This implies :option:`-j`. + +.. option:: -c class + + This option specifies the class of the zone. If not specified, ``IN`` is assumed. + +.. option:: -i mode + + This option performs post-load zone integrity checks. Possible modes are + ``full`` (the default), ``full-sibling``, ``local``, + ``local-sibling``, and ``none``. + + Mode ``full`` checks that MX records refer to A or AAAA records + (both in-zone and out-of-zone hostnames). Mode ``local`` only + checks MX records which refer to in-zone hostnames. + + Mode ``full`` checks that SRV records refer to A or AAAA records + (both in-zone and out-of-zone hostnames). Mode ``local`` only + checks SRV records which refer to in-zone hostnames. + + Mode ``full`` checks that delegation NS records refer to A or AAAA + records (both in-zone and out-of-zone hostnames). It also checks that + glue address records in the zone match those advertised by the child. + Mode ``local`` only checks NS records which refer to in-zone + hostnames or verifies that some required glue exists, i.e., when the + name server is in a child zone. + + Modes ``full-sibling`` and ``local-sibling`` disable sibling glue + checks, but are otherwise the same as ``full`` and ``local``, + respectively. + + Mode ``none`` disables the checks. + +.. option:: -f format + + This option specifies the format of the zone file. Possible formats are + ``text`` (the default), and ``raw``. + +.. option:: -F format + + This option specifies the format of the output file specified. For + :iscman:`named-checkzone`, this does not have any effect unless it dumps + the zone contents. + + Possible formats are ``text`` (the default), which is the standard + textual representation of the zone, and ``raw`` and ``raw=N``, which + store the zone in a binary format for rapid loading by :iscman:`named`. + ``raw=N`` specifies the format version of the raw zone file: if ``N`` is + 0, the raw file can be read by any version of :iscman:`named`; if N is 1, the + file can only be read by release 9.9.0 or higher. The default is 1. + +.. option:: -k mode + + This option performs ``check-names`` checks with the specified failure mode. + Possible modes are ``fail`` (the default), ``warn``, and ``ignore``. + +.. option:: -l ttl + + This option sets a maximum permissible TTL for the input file. Any record with a + TTL higher than this value causes the zone to be rejected. This + is similar to using the ``max-zone-ttl`` option in :iscman:`named.conf`. + +.. option:: -L serial + + When compiling a zone to ``raw`` format, this option sets the "source + serial" value in the header to the specified serial number. This is + expected to be used primarily for testing purposes. + +.. option:: -m mode + + This option specifies whether MX records should be checked to see if they are + addresses. Possible modes are ``fail``, ``warn`` (the default), and + ``ignore``. + +.. option:: -M mode + + This option checks whether a MX record refers to a CNAME. Possible modes are + ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -n mode + + This option specifies whether NS records should be checked to see if they are + addresses. Possible modes are ``fail`` (the default), ``warn``, and + ``ignore``. + +.. option:: -o filename + + This option writes the zone output to ``filename``. If ``filename`` is ``-``, then + the zone output is written to standard output. This is mandatory for :program:`named-compilezone`. + +.. option:: -r mode + + This option checks for records that are treated as different by DNSSEC but are + semantically equal in plain DNS. Possible modes are ``fail``, + ``warn`` (the default), and ``ignore``. + +.. option:: -s style + + This option specifies the style of the dumped zone file. Possible styles are + ``full`` (the default) and ``relative``. The ``full`` format is most + suitable for processing automatically by a separate script. + The relative format is more human-readable and is thus + suitable for editing by hand. + +.. option:: -S mode + + This option checks whether an SRV record refers to a CNAME. Possible modes are + ``fail``, ``warn`` (the default), and ``ignore``. + +.. option:: -t directory + + This option tells :iscman:`named` to chroot to ``directory``, so that ``include`` directives in the + configuration file are processed as if run by a similarly chrooted + :iscman:`named`. + +.. option:: -T mode + + This option checks whether Sender Policy Framework (SPF) records exist and issues a + warning if an SPF-formatted TXT record is not also present. Possible + modes are ``warn`` (the default) and ``ignore``. + +.. option:: -w directory + + This option instructs :iscman:`named` to chdir to ``directory``, so that relative filenames in master file + ``$INCLUDE`` directives work. This is similar to the directory clause in + :iscman:`named.conf`. + +.. option:: -D + + This option dumps the zone file in canonical format. This is always enabled for + :program:`named-compilezone`. + +.. option:: -W mode + + This option specifies whether to check for non-terminal wildcards. Non-terminal + wildcards are almost always the result of a failure to understand the + wildcard matching algorithm (:rfc:`4592`). Possible modes are ``warn`` + (the default) and ``ignore``. + +.. option:: zonename + + This indicates the domain name of the zone being checked. + +.. option:: filename + + This is the name of the zone file. + +Return Values +~~~~~~~~~~~~~ + +:program:`named-compilezone` returns an exit status of 1 if errors were detected +and 0 otherwise. + +See Also +~~~~~~~~ + +:iscman:`named(8) `, :iscman:`named-checkconf(8) `, :iscman:`named-checkzone(8) `, :rfc:`1035`, +BIND 9 Administrator Reference Manual. diff --git a/bin/confgen/Makefile.am b/bin/confgen/Makefile.am new file mode 100644 index 0000000..c1dca43 --- /dev/null +++ b/bin/confgen/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + -DRNDC_KEYFILE=\"${sysconfdir}/rndc.key\" + +LDADD += \ + libconfgen.la \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +noinst_LTLIBRARIES = libconfgen.la + +libconfgen_la_SOURCES = \ + include/confgen/os.h \ + keygen.c \ + keygen.h \ + os.c \ + util.c \ + util.h + +sbin_PROGRAMS = tsig-keygen rndc-confgen + +install-exec-hook: + ln -f $(DESTDIR)$(sbindir)/tsig-keygen \ + $(DESTDIR)$(sbindir)/ddns-confgen + +uninstall-hook: + -rm -f $(DESTDIR)$(sbindir)/ddns-confgen diff --git a/bin/confgen/Makefile.in b/bin/confgen/Makefile.in new file mode 100644 index 0000000..5589f9e --- /dev/null +++ b/bin/confgen/Makefile.in @@ -0,0 +1,876 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +sbin_PROGRAMS = tsig-keygen$(EXEEXT) rndc-confgen$(EXEEXT) +subdir = bin/confgen +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libconfgen_la_LIBADD = +am_libconfgen_la_OBJECTS = keygen.lo os.lo util.lo +libconfgen_la_OBJECTS = $(am_libconfgen_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +rndc_confgen_SOURCES = rndc-confgen.c +rndc_confgen_OBJECTS = rndc-confgen.$(OBJEXT) +rndc_confgen_LDADD = $(LDADD) +rndc_confgen_DEPENDENCIES = libconfgen.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +tsig_keygen_SOURCES = tsig-keygen.c +tsig_keygen_OBJECTS = tsig-keygen.$(OBJEXT) +tsig_keygen_LDADD = $(LDADD) +tsig_keygen_DEPENDENCIES = libconfgen.la $(LIBISC_LIBS) $(LIBDNS_LIBS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/keygen.Plo ./$(DEPDIR)/os.Plo \ + ./$(DEPDIR)/rndc-confgen.Po ./$(DEPDIR)/tsig-keygen.Po \ + ./$(DEPDIR)/util.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libconfgen_la_SOURCES) rndc-confgen.c tsig-keygen.c +DIST_SOURCES = $(libconfgen_la_SOURCES) rndc-confgen.c tsig-keygen.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + -DRNDC_KEYFILE=\"${sysconfdir}/rndc.key\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = libconfgen.la $(LIBISC_LIBS) $(LIBDNS_LIBS) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +noinst_LTLIBRARIES = libconfgen.la +libconfgen_la_SOURCES = \ + include/confgen/os.h \ + keygen.c \ + keygen.h \ + os.c \ + util.c \ + util.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/confgen/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/confgen/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libconfgen.la: $(libconfgen_la_OBJECTS) $(libconfgen_la_DEPENDENCIES) $(EXTRA_libconfgen_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libconfgen_la_OBJECTS) $(libconfgen_la_LIBADD) $(LIBS) + +rndc-confgen$(EXEEXT): $(rndc_confgen_OBJECTS) $(rndc_confgen_DEPENDENCIES) $(EXTRA_rndc_confgen_DEPENDENCIES) + @rm -f rndc-confgen$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rndc_confgen_OBJECTS) $(rndc_confgen_LDADD) $(LIBS) + +tsig-keygen$(EXEEXT): $(tsig_keygen_OBJECTS) $(tsig_keygen_DEPENDENCIES) $(EXTRA_tsig_keygen_DEPENDENCIES) + @rm -f tsig-keygen$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tsig_keygen_OBJECTS) $(tsig_keygen_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keygen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rndc-confgen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsig-keygen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/keygen.Plo + -rm -f ./$(DEPDIR)/os.Plo + -rm -f ./$(DEPDIR)/rndc-confgen.Po + -rm -f ./$(DEPDIR)/tsig-keygen.Po + -rm -f ./$(DEPDIR)/util.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-exec-hook +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/keygen.Plo + -rm -f ./$(DEPDIR)/os.Plo + -rm -f ./$(DEPDIR)/rndc-confgen.Po + -rm -f ./$(DEPDIR)/tsig-keygen.Po + -rm -f ./$(DEPDIR)/util.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-sbinPROGRAMS + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-hook +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-exec-am install-strip uninstall-am + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-exec-hook install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-hook uninstall-sbinPROGRAMS unit-am \ + unit-local + +.PRECIOUS: Makefile + + +install-exec-hook: + ln -f $(DESTDIR)$(sbindir)/tsig-keygen \ + $(DESTDIR)$(sbindir)/ddns-confgen + +uninstall-hook: + -rm -f $(DESTDIR)$(sbindir)/ddns-confgen + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/confgen/ddns-confgen.rst b/bin/confgen/ddns-confgen.rst new file mode 100644 index 0000000..9dd9d5e --- /dev/null +++ b/bin/confgen/ddns-confgen.rst @@ -0,0 +1,96 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. BEWARE: Do not forget to edit also tsig-keygen.rst! + +.. iscman:: ddns-confgen +.. program:: ddns-confgen +.. _man_ddns-confgen: + +ddns-confgen - TSIG key generation tool +--------------------------------------- + +Synopsis +~~~~~~~~ +:program:`ddns-confgen` [**-a** algorithm] [**-h**] [**-k** keyname] [**-q**] [**-s** name] [**-z** zone] + +Description +~~~~~~~~~~~ + +:program:`ddns-confgen` is an utility that generates keys for use in TSIG signing. +The resulting keys can be used, for example, to secure dynamic DNS updates +to a zone, or for the :iscman:`rndc` command channel. + +The key name can specified using :option:`-k` parameter and defaults to ``ddns-key``. +The generated key is accompanied by configuration text and instructions that +can be used with :iscman:`nsupdate` and :iscman:`named` when setting up dynamic DNS, +including an example ``update-policy`` statement. +(This usage is similar to the :iscman:`rndc-confgen` command for setting up +command-channel security.) + +Note that :iscman:`named` itself can configure a local DDNS key for use with +:option:`nsupdate -l`; it does this when a zone is configured with +``update-policy local;``. :program:`ddns-confgen` is only needed when a more +elaborate configuration is required: for instance, if :iscman:`nsupdate` is to +be used from a remote system. + +Options +~~~~~~~ + +.. option:: -a algorithm + + This option specifies the algorithm to use for the TSIG key. Available + choices are: hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, + and hmac-sha512. The default is hmac-sha256. Options are + case-insensitive, and the "hmac-" prefix may be omitted. + +.. option:: -h + + This option prints a short summary of options and arguments. + +.. option:: -k keyname + + This option specifies the key name of the DDNS authentication key. The + default is ``ddns-key`` when neither the :option:`-s` nor :option:`-z` option is + specified; otherwise, the default is ``ddns-key`` as a separate label + followed by the argument of the option, e.g., ``ddns-key.example.com.`` + The key name must have the format of a valid domain name, consisting of + letters, digits, hyphens, and periods. + +.. option:: -q + + This option enables quiet mode, which prints only the key, with no + explanatory text or usage examples. This is essentially identical to + :iscman:`tsig-keygen`. + +.. option:: -s name + + This option generates a configuration example to allow dynamic updates + of a single hostname. The example :iscman:`named.conf` text shows how to set + an update policy for the specified name using the "name" nametype. The + default key name is ``ddns-key.name``. Note that the "self" nametype + cannot be used, since the name to be updated may differ from the key + name. This option cannot be used with the :option:`-z` option. + +.. option:: -z zone + + This option generates a configuration example to allow + dynamic updates of a zone. The example :iscman:`named.conf` text shows how + to set an update policy for the specified zone using the "zonesub" + nametype, allowing updates to all subdomain names within that zone. + This option cannot be used with the :option:`-s` option. + +See Also +~~~~~~~~ + +:iscman:`nsupdate(1) `, :iscman:`named.conf(5) `, :iscman:`named(8) `, BIND 9 Administrator Reference Manual. diff --git a/bin/confgen/include/confgen/os.h b/bin/confgen/include/confgen/os.h new file mode 100644 index 0000000..2a924a3 --- /dev/null +++ b/bin/confgen/include/confgen/os.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#pragma once + +#include + +#include + +ISC_LANG_BEGINDECLS + +int +set_user(FILE *fd, const char *user); +/*%< + * Set the owner of the file referenced by 'fd' to 'user'. + * Returns: + * 0 success + * -1 insufficient permissions, or 'user' does not exist. + */ + +ISC_LANG_ENDDECLS diff --git a/bin/confgen/keygen.c b/bin/confgen/keygen.c new file mode 100644 index 0000000..97dfa34 --- /dev/null +++ b/bin/confgen/keygen.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include "keygen.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "util.h" + +/*% + * Convert string to algorithm type. + */ +dns_secalg_t +alg_fromtext(const char *name) { + const char *p = name; + if (strncasecmp(p, "hmac-", 5) == 0) { + p = &name[5]; + } + + if (strcasecmp(p, "md5") == 0) { + return (DST_ALG_HMACMD5); + } + if (strcasecmp(p, "sha1") == 0) { + return (DST_ALG_HMACSHA1); + } + if (strcasecmp(p, "sha224") == 0) { + return (DST_ALG_HMACSHA224); + } + if (strcasecmp(p, "sha256") == 0) { + return (DST_ALG_HMACSHA256); + } + if (strcasecmp(p, "sha384") == 0) { + return (DST_ALG_HMACSHA384); + } + if (strcasecmp(p, "sha512") == 0) { + return (DST_ALG_HMACSHA512); + } + return (DST_ALG_UNKNOWN); +} + +/*% + * Return default keysize for a given algorithm type. + */ +int +alg_bits(dns_secalg_t alg) { + switch (alg) { + case DST_ALG_HMACMD5: + return (128); + case DST_ALG_HMACSHA1: + return (160); + case DST_ALG_HMACSHA224: + return (224); + case DST_ALG_HMACSHA256: + return (256); + case DST_ALG_HMACSHA384: + return (384); + case DST_ALG_HMACSHA512: + return (512); + default: + return (0); + } +} + +/*% + * Generate a key of size 'keysize' and place it in 'key_txtbuffer' + */ +void +generate_key(isc_mem_t *mctx, dns_secalg_t alg, int keysize, + isc_buffer_t *key_txtbuffer) { + isc_result_t result = ISC_R_SUCCESS; + isc_buffer_t key_rawbuffer; + isc_region_t key_rawregion; + char key_rawsecret[64]; + dst_key_t *key = NULL; + + switch (alg) { + case DST_ALG_HMACMD5: + case DST_ALG_HMACSHA1: + case DST_ALG_HMACSHA224: + case DST_ALG_HMACSHA256: + if (keysize < 1 || keysize > 512) { + fatal("keysize %d out of range (must be 1-512)\n", + keysize); + } + break; + case DST_ALG_HMACSHA384: + case DST_ALG_HMACSHA512: + if (keysize < 1 || keysize > 1024) { + fatal("keysize %d out of range (must be 1-1024)\n", + keysize); + } + break; + default: + fatal("unsupported algorithm %d\n", alg); + } + + DO("initialize dst library", dst_lib_init(mctx, NULL)); + + DO("generate key", + dst_key_generate(dns_rootname, alg, keysize, 0, 0, DNS_KEYPROTO_ANY, + dns_rdataclass_in, mctx, &key, NULL)); + + isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); + + DO("dump key to buffer", dst_key_tobuffer(key, &key_rawbuffer)); + + isc_buffer_usedregion(&key_rawbuffer, &key_rawregion); + + DO("bsse64 encode secret", + isc_base64_totext(&key_rawregion, -1, "", key_txtbuffer)); + + if (key != NULL) { + dst_key_free(&key); + } + + dst_lib_destroy(); +} + +/*% + * Write a key file to 'keyfile'. If 'user' is non-NULL, + * make that user the owner of the file. The key will have + * the name 'keyname' and the secret in the buffer 'secret'. + */ +void +write_key_file(const char *keyfile, const char *user, const char *keyname, + isc_buffer_t *secret, dns_secalg_t alg) { + isc_result_t result; + const char *algname = dst_hmac_algorithm_totext(alg); + FILE *fd = NULL; + + DO("create keyfile", isc_file_safecreate(keyfile, &fd)); + + if (user != NULL) { + if (set_user(fd, user) == -1) { + fatal("unable to set file owner\n"); + } + } + + fprintf(fd, + "key \"%s\" {\n\talgorithm %s;\n" + "\tsecret \"%.*s\";\n};\n", + keyname, algname, (int)isc_buffer_usedlength(secret), + (char *)isc_buffer_base(secret)); + fflush(fd); + if (ferror(fd)) { + fatal("write to %s failed\n", keyfile); + } + if (fclose(fd)) { + fatal("fclose(%s) failed\n", keyfile); + } +} diff --git a/bin/confgen/keygen.h b/bin/confgen/keygen.h new file mode 100644 index 0000000..f5b7a55 --- /dev/null +++ b/bin/confgen/keygen.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +void +generate_key(isc_mem_t *mctx, dns_secalg_t alg, int keysize, + isc_buffer_t *key_txtbuffer); + +void +write_key_file(const char *keyfile, const char *user, const char *keyname, + isc_buffer_t *secret, dns_secalg_t alg); + +const char * +alg_totext(dns_secalg_t alg); +dns_secalg_t +alg_fromtext(const char *name); +int +alg_bits(dns_secalg_t alg); + +ISC_LANG_ENDDECLS diff --git a/bin/confgen/os.c b/bin/confgen/os.c new file mode 100644 index 0000000..445d64b --- /dev/null +++ b/bin/confgen/os.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int +set_user(FILE *fd, const char *user) { + struct passwd *pw; + + pw = getpwnam(user); + if (pw == NULL) { + errno = EINVAL; + return (-1); + } + return (fchown(fileno(fd), pw->pw_uid, -1)); +} diff --git a/bin/confgen/rndc-confgen.c b/bin/confgen/rndc-confgen.c new file mode 100644 index 0000000..8179295 --- /dev/null +++ b/bin/confgen/rndc-confgen.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/** + * rndc-confgen generates configuration files for rndc. It can be used + * as a convenient alternative to writing the rndc.conf file and the + * corresponding controls and key statements in named.conf by hand. + * Alternatively, it can be run with the -a option to set up a + * rndc.key file and avoid the need for a rndc.conf file and a + * controls statement altogether. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "keygen.h" +#include "util.h" + +#define DEFAULT_KEYNAME "rndc-key" +#define DEFAULT_SERVER "127.0.0.1" +#define DEFAULT_PORT 953 + +static char program[256]; +const char *progname; + +bool verbose = false; + +const char *keyfile, *keydef; + +noreturn static void +usage(int status); + +static void +usage(int status) { + fprintf(stderr, "\ +Usage:\n\ + %s [-a] [-b bits] [-c keyfile] [-k keyname] [-p port] \ +[-s addr] [-t chrootdir] [-u user]\n\ + -a: generate just the key clause and write it to keyfile (%s)\n\ + -A alg: algorithm (default hmac-sha256)\n\ + -b bits: from 1 through 512, default 256; total length of the secret\n\ + -c keyfile: specify an alternate key file (requires -a)\n\ + -k keyname: the name as it will be used in named.conf and rndc.conf\n\ + -p port: the port named will listen on and rndc will connect to\n\ + -q: suppress printing written key path\n\ + -s addr: the address to which rndc should connect\n\ + -t chrootdir: write a keyfile in chrootdir as well (requires -a)\n\ + -u user: set the keyfile owner to \"user\" (requires -a)\n", + progname, keydef); + + exit(status); +} + +int +main(int argc, char **argv) { + bool show_final_mem = false; + isc_buffer_t key_txtbuffer; + char key_txtsecret[256]; + isc_mem_t *mctx = NULL; + isc_result_t result = ISC_R_SUCCESS; + const char *keyname = NULL; + const char *serveraddr = NULL; + dns_secalg_t alg; + const char *algname; + char *p; + int ch; + int port; + int keysize = -1; + struct in_addr addr4_dummy; + struct in6_addr addr6_dummy; + char *chrootdir = NULL; + char *user = NULL; + bool keyonly = false; + bool quiet = false; + int len; + + keydef = keyfile = RNDC_KEYFILE; + + result = isc_file_progname(*argv, program, sizeof(program)); + if (result != ISC_R_SUCCESS) { + memmove(program, "rndc-confgen", 13); + } + progname = program; + + keyname = DEFAULT_KEYNAME; + alg = DST_ALG_HMACSHA256; + serveraddr = DEFAULT_SERVER; + port = DEFAULT_PORT; + + isc_commandline_errprint = false; + + while ((ch = isc_commandline_parse(argc, argv, + "aA:b:c:hk:Mmp:r:s:t:u:Vy")) != -1) + { + switch (ch) { + case 'a': + keyonly = true; + break; + case 'A': + algname = isc_commandline_argument; + alg = alg_fromtext(algname); + if (alg == DST_ALG_UNKNOWN) { + fatal("Unsupported algorithm '%s'", algname); + } + break; + case 'b': + keysize = strtol(isc_commandline_argument, &p, 10); + if (*p != '\0' || keysize < 0) { + fatal("-b requires a non-negative number"); + } + break; + case 'c': + keyfile = isc_commandline_argument; + break; + case 'h': + usage(0); + case 'k': + case 'y': /* Compatible with rndc -y. */ + keyname = isc_commandline_argument; + break; + case 'M': + isc_mem_debugging = ISC_MEM_DEBUGTRACE; + break; + + case 'm': + show_final_mem = true; + break; + case 'p': + port = strtol(isc_commandline_argument, &p, 10); + if (*p != '\0' || port < 0 || port > 65535) { + fatal("port '%s' out of range", + isc_commandline_argument); + } + break; + case 'q': + quiet = true; + break; + case 'r': + fatal("The -r option has been deprecated."); + break; + case 's': + serveraddr = isc_commandline_argument; + if (inet_pton(AF_INET, serveraddr, &addr4_dummy) != 1 && + inet_pton(AF_INET6, serveraddr, &addr6_dummy) != 1) + { + fatal("-s should be an IPv4 or IPv6 address"); + } + break; + case 't': + chrootdir = isc_commandline_argument; + break; + case 'u': + user = isc_commandline_argument; + break; + case 'V': + verbose = true; + break; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + usage(1); + } else { + usage(0); + } + break; + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + POST(argv); + + if (argc > 0) { + usage(1); + } + + if (alg == DST_ALG_HMACMD5) { + fprintf(stderr, "warning: use of hmac-md5 for RNDC keys " + "is deprecated; hmac-sha256 is now " + "recommended.\n"); + } + + if (keysize < 0) { + keysize = alg_bits(alg); + } + algname = dst_hmac_algorithm_totext(alg); + + isc_mem_create(&mctx); + isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); + + generate_key(mctx, alg, keysize, &key_txtbuffer); + + if (keyonly) { + write_key_file(keyfile, chrootdir == NULL ? user : NULL, + keyname, &key_txtbuffer, alg); + if (!quiet) { + printf("wrote key file \"%s\"\n", keyfile); + } + + if (chrootdir != NULL) { + char *buf; + len = strlen(chrootdir) + strlen(keyfile) + 2; + buf = isc_mem_get(mctx, len); + snprintf(buf, len, "%s%s%s", chrootdir, + (*keyfile != '/') ? "/" : "", keyfile); + + write_key_file(buf, user, keyname, &key_txtbuffer, alg); + if (!quiet) { + printf("wrote key file \"%s\"\n", buf); + } + isc_mem_put(mctx, buf, len); + } + } else { + printf("\ +# Start of rndc.conf\n\ +key \"%s\" {\n\ + algorithm %s;\n\ + secret \"%.*s\";\n\ +};\n\ +\n\ +options {\n\ + default-key \"%s\";\n\ + default-server %s;\n\ + default-port %d;\n\ +};\n\ +# End of rndc.conf\n\ +\n\ +# Use with the following in named.conf, adjusting the allow list as needed:\n\ +# key \"%s\" {\n\ +# algorithm %s;\n\ +# secret \"%.*s\";\n\ +# };\n\ +# \n\ +# controls {\n\ +# inet %s port %d\n\ +# allow { %s; } keys { \"%s\"; };\n\ +# };\n\ +# End of named.conf\n", + keyname, algname, + (int)isc_buffer_usedlength(&key_txtbuffer), + (char *)isc_buffer_base(&key_txtbuffer), keyname, + serveraddr, port, keyname, algname, + (int)isc_buffer_usedlength(&key_txtbuffer), + (char *)isc_buffer_base(&key_txtbuffer), serveraddr, + port, serveraddr, keyname); + } + + if (show_final_mem) { + isc_mem_stats(mctx, stderr); + } + + isc_mem_destroy(&mctx); + + return (0); +} diff --git a/bin/confgen/rndc-confgen.rst b/bin/confgen/rndc-confgen.rst new file mode 100644 index 0000000..0a91489 --- /dev/null +++ b/bin/confgen/rndc-confgen.rst @@ -0,0 +1,121 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: rndc-confgen +.. program:: rndc-confgen +.. _man_rndc-confgen: + +rndc-confgen - rndc key generation tool +--------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`rndc-confgen` [**-a**] [**-A** algorithm] [**-b** keysize] [**-c** keyfile] [**-h**] [**-k** keyname] [**-p** port] [**-s** address] [**-t** chrootdir] [**-u** user] + +Description +~~~~~~~~~~~ + +:program:`rndc-confgen` generates configuration files for :iscman:`rndc`. It can be +used as a convenient alternative to writing the :iscman:`rndc.conf` file and +the corresponding ``controls`` and ``key`` statements in :iscman:`named.conf` +by hand. Alternatively, it can be run with the :option:`-a` option to set up a +``rndc.key`` file and avoid the need for a :iscman:`rndc.conf` file and a +``controls`` statement altogether. + +Options +~~~~~~~ + +.. option:: -a + + This option sets automatic :iscman:`rndc` configuration, which creates a file + |rndc_key| that is read by both :iscman:`rndc` and :iscman:`named` on startup. + The ``rndc.key`` file defines a default command channel and + authentication key allowing :iscman:`rndc` to communicate with :iscman:`named` on + the local host with no further configuration. + + If a more elaborate configuration than that generated by + :option:`rndc-confgen -a` is required, for example if rndc is to be used + remotely, run :program:`rndc-confgen` without the :option:`-a` option + and set up :iscman:`rndc.conf` and :iscman:`named.conf` as directed. + +.. option:: -A algorithm + + This option specifies the algorithm to use for the TSIG key. Available choices + are: hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, and + hmac-sha512. The default is hmac-sha256. + +.. option:: -b keysize + + This option specifies the size of the authentication key in bits. The size must be between + 1 and 512 bits; the default is the hash size. + +.. option:: -c keyfile + + This option is used with the :option:`-a` option to specify an alternate location for + ``rndc.key``. + +.. option:: -h + + This option prints a short summary of the options and arguments to + :program:`rndc-confgen`. + +.. option:: -k keyname + + This option specifies the key name of the :iscman:`rndc` authentication key. This must be a + valid domain name. The default is ``rndc-key``. + +.. option:: -p port + + This option specifies the command channel port where :iscman:`named` listens for + connections from :iscman:`rndc`. The default is 953. + +.. option:: -q + + This option prevets printing the written path in automatic configuration mode. + +.. option:: -s address + + This option specifies the IP address where :iscman:`named` listens for command-channel + connections from :iscman:`rndc`. The default is the loopback address + 127.0.0.1. + +.. option:: -t chrootdir + + This option is used with the :option:`-a` option to specify a directory where :iscman:`named` + runs chrooted. An additional copy of the ``rndc.key`` is + written relative to this directory, so that it is found by the + chrooted :iscman:`named`. + +.. option:: -u user + + This option is used with the :option:`-a` option to set the owner of the generated ``rndc.key`` file. + If :option:`-t` is also specified, only the file in the chroot + area has its owner changed. + +Examples +~~~~~~~~ + +To allow :iscman:`rndc` to be used with no manual configuration, run: + +``rndc-confgen -a`` + +To print a sample :iscman:`rndc.conf` file and the corresponding ``controls`` and +``key`` statements to be manually inserted into :iscman:`named.conf`, run: + +:program:`rndc-confgen` + +See Also +~~~~~~~~ + +:iscman:`rndc(8) `, :iscman:`rndc.conf(5) `, :iscman:`named(8) `, BIND 9 Administrator Reference Manual. diff --git a/bin/confgen/tsig-keygen.c b/bin/confgen/tsig-keygen.c new file mode 100644 index 0000000..b44f1a9 --- /dev/null +++ b/bin/confgen/tsig-keygen.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/** + * tsig-keygen generates TSIG keys that can be used in named configuration + * files for dynamic DNS. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "keygen.h" +#include "util.h" + +#define KEYGEN_DEFAULT "tsig-key" +#define CONFGEN_DEFAULT "ddns-key" + +static char program[256]; +const char *progname; +static enum { progmode_keygen, progmode_confgen } progmode; +bool verbose = false; /* needed by util.c but not used here */ + +noreturn static void +usage(int status); + +static void +usage(int status) { + if (progmode == progmode_confgen) { + fprintf(stderr, "\ +Usage:\n\ + %s [-a alg] [-k keyname] [-q] [-s name | -z zone]\n\ + -a alg: algorithm (default hmac-sha256)\n\ + -k keyname: name of the key as it will be used in named.conf\n\ + -s name: domain name to be updated using the created key\n\ + -z zone: name of the zone as it will be used in named.conf\n\ + -q: quiet mode: print the key, with no explanatory text\n", + progname); + } else { + fprintf(stderr, "\ +Usage:\n\ + %s [-a alg] [keyname]\n\ + -a alg: algorithm (default hmac-sha256)\n\n", + progname); + } + + exit(status); +} + +int +main(int argc, char **argv) { + isc_result_t result = ISC_R_SUCCESS; + bool show_final_mem = false; + bool quiet = false; + isc_buffer_t key_txtbuffer; + char key_txtsecret[256]; + isc_mem_t *mctx = NULL; + const char *keyname = NULL; + const char *zone = NULL; + const char *self_domain = NULL; + char *keybuf = NULL; + dns_secalg_t alg = DST_ALG_HMACSHA256; + const char *algname; + int keysize = 256; + int len = 0; + int ch; + + result = isc_file_progname(*argv, program, sizeof(program)); + if (result != ISC_R_SUCCESS) { + memmove(program, "tsig-keygen", 11); + } + progname = program; + + /* + * Libtool doesn't preserve the program name prior to final + * installation. Remove the libtool prefix ("lt-"). + */ + if (strncmp(progname, "lt-", 3) == 0) { + progname += 3; + } + +#define PROGCMP(X) \ + (strcasecmp(progname, X) == 0 || strcasecmp(progname, X ".exe") == 0) + + if (PROGCMP("tsig-keygen")) { + progmode = progmode_keygen; + quiet = true; + } else if (PROGCMP("ddns-confgen")) { + progmode = progmode_confgen; + } else { + UNREACHABLE(); + } + + isc_commandline_errprint = false; + + while ((ch = isc_commandline_parse(argc, argv, "a:hk:Mmr:qs:y:z:")) != + -1) + { + switch (ch) { + case 'a': + algname = isc_commandline_argument; + alg = alg_fromtext(algname); + if (alg == DST_ALG_UNKNOWN) { + fatal("Unsupported algorithm '%s'", algname); + } + keysize = alg_bits(alg); + break; + case 'h': + usage(0); + case 'k': + case 'y': + if (progmode == progmode_confgen) { + keyname = isc_commandline_argument; + } else { + usage(1); + } + break; + case 'M': + isc_mem_debugging = ISC_MEM_DEBUGTRACE; + break; + case 'm': + show_final_mem = true; + break; + case 'q': + if (progmode == progmode_confgen) { + quiet = true; + } else { + usage(1); + } + break; + case 'r': + fatal("The -r option has been deprecated."); + break; + case 's': + if (progmode == progmode_confgen) { + self_domain = isc_commandline_argument; + } else { + usage(1); + } + break; + case 'z': + if (progmode == progmode_confgen) { + zone = isc_commandline_argument; + } else { + usage(1); + } + break; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + usage(1); + } else { + usage(0); + } + break; + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + if (progmode == progmode_keygen) { + keyname = argv[isc_commandline_index++]; + } + + POST(argv); + + if (self_domain != NULL && zone != NULL) { + usage(1); /* -s and -z cannot coexist */ + } + + if (argc > isc_commandline_index) { + usage(1); + } + + /* Use canonical algorithm name */ + algname = dst_hmac_algorithm_totext(alg); + + isc_mem_create(&mctx); + + if (keyname == NULL) { + const char *suffix = NULL; + + keyname = ((progmode == progmode_keygen) ? KEYGEN_DEFAULT + : CONFGEN_DEFAULT); + if (self_domain != NULL) { + suffix = self_domain; + } else if (zone != NULL) { + suffix = zone; + } + if (suffix != NULL) { + len = strlen(keyname) + strlen(suffix) + 2; + keybuf = isc_mem_get(mctx, len); + snprintf(keybuf, len, "%s.%s", keyname, suffix); + keyname = (const char *)keybuf; + } + } + + isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); + + generate_key(mctx, alg, keysize, &key_txtbuffer); + + if (!quiet) { + printf("\ +# To activate this key, place the following in named.conf, and\n\ +# in a separate keyfile on the system or systems from which nsupdate\n\ +# will be run:\n"); + } + + printf("\ +key \"%s\" {\n\ + algorithm %s;\n\ + secret \"%.*s\";\n\ +};\n", + keyname, algname, (int)isc_buffer_usedlength(&key_txtbuffer), + (char *)isc_buffer_base(&key_txtbuffer)); + + if (!quiet) { + if (self_domain != NULL) { + printf("\n\ +# Then, in the \"zone\" statement for the zone containing the\n\ +# name \"%s\", place an \"update-policy\" statement\n\ +# like this one, adjusted as needed for your preferred permissions:\n\ +update-policy {\n\ + grant %s name %s ANY;\n\ +};\n", + self_domain, keyname, self_domain); + } else if (zone != NULL) { + printf("\n\ +# Then, in the \"zone\" definition statement for \"%s\",\n\ +# place an \"update-policy\" statement like this one, adjusted as \n\ +# needed for your preferred permissions:\n\ +update-policy {\n\ + grant %s zonesub ANY;\n\ +};\n", + zone, keyname); + } else { + printf("\n\ +# Then, in the \"zone\" statement for each zone you wish to dynamically\n\ +# update, place an \"update-policy\" statement granting update permission\n\ +# to this key. For example, the following statement grants this key\n\ +# permission to update any name within the zone:\n\ +update-policy {\n\ + grant %s zonesub ANY;\n\ +};\n", + keyname); + } + + printf("\n\ +# After the keyfile has been placed, the following command will\n\ +# execute nsupdate using this key:\n\ +nsupdate -k \n"); + } + + if (keybuf != NULL) { + isc_mem_put(mctx, keybuf, len); + } + + if (show_final_mem) { + isc_mem_stats(mctx, stderr); + } + + isc_mem_destroy(&mctx); + + return (0); +} diff --git a/bin/confgen/tsig-keygen.rst b/bin/confgen/tsig-keygen.rst new file mode 100644 index 0000000..7420bed --- /dev/null +++ b/bin/confgen/tsig-keygen.rst @@ -0,0 +1,54 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. BEWARE: Do not forget to edit also ddns-confgen.rst! + +.. iscman:: tsig-keygen +.. program:: tsig-keygen +.. _man_tsig-keygen: + +tsig-keygen - TSIG key generation tool +-------------------------------------- + +Synopsis +~~~~~~~~ +:program:`tsig-keygen` [**-a** algorithm] [**-h**] [name] + +Description +~~~~~~~~~~~ + +:program:`tsig-keygen` is an utility that generates keys for use in TSIG signing. +The resulting keys can be used, for example, to secure dynamic DNS updates +to a zone, or for the :iscman:`rndc` command channel. + +A domain name can be specified on the command line to be used as the name +of the generated key. If no name is specified, the default is ``tsig-key``. + +Options +~~~~~~~ + +.. option:: -a algorithm + + This option specifies the algorithm to use for the TSIG key. Available + choices are: hmac-md5, hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, + and hmac-sha512. The default is hmac-sha256. Options are + case-insensitive, and the "hmac-" prefix may be omitted. + +.. option:: -h + + This option prints a short summary of options and arguments. + +See Also +~~~~~~~~ + +:iscman:`nsupdate(1) `, :iscman:`named.conf(5) `, :iscman:`named(8) `, BIND 9 Administrator Reference Manual. diff --git a/bin/confgen/util.c b/bin/confgen/util.c new file mode 100644 index 0000000..b0085fe --- /dev/null +++ b/bin/confgen/util.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include "util.h" +#include +#include +#include +#include + +#include + +extern bool verbose; +extern const char *progname; + +void +notify(const char *fmt, ...) { + va_list ap; + + if (verbose) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } +} + +void +fatal(const char *format, ...) { + va_list args; + + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} diff --git a/bin/confgen/util.h b/bin/confgen/util.h new file mode 100644 index 0000000..4ac83e5 --- /dev/null +++ b/bin/confgen/util.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include +#include + +#define NS_CONTROL_PORT 953 + +#undef DO +#define DO(name, function) \ + do { \ + result = function; \ + if (result != ISC_R_SUCCESS) \ + fatal("%s: %s", name, isc_result_totext(result)); \ + else \ + notify("%s", name); \ + } while (0) + +ISC_LANG_BEGINDECLS + +void +notify(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2); + +noreturn void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +ISC_LANG_ENDDECLS diff --git a/bin/delv/Makefile.am b/bin/delv/Makefile.am new file mode 100644 index 0000000..5405023 --- /dev/null +++ b/bin/delv/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + -I$(top_builddir)/include \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBIRS_CFLAGS) + +AM_CPPFLAGS += \ + -DSYSCONFDIR=\"${sysconfdir}\" + +bin_PROGRAMS = delv + +delv_SOURCES = \ + delv.c +delv_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) diff --git a/bin/delv/Makefile.in b/bin/delv/Makefile.in new file mode 100644 index 0000000..b86bfb5 --- /dev/null +++ b/bin/delv/Makefile.in @@ -0,0 +1,823 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +bin_PROGRAMS = delv$(EXEEXT) +subdir = bin/delv +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_delv_OBJECTS = delv.$(OBJEXT) +delv_OBJECTS = $(am_delv_OBJECTS) +delv_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/delv.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(delv_SOURCES) +DIST_SOURCES = $(delv_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include -I$(top_builddir)/include $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) $(LIBISCCFG_CFLAGS) $(LIBIRS_CFLAGS) \ + -DSYSCONFDIR=\"${sysconfdir}\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +delv_SOURCES = \ + delv.c + +delv_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/delv/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/delv/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +delv$(EXEEXT): $(delv_OBJECTS) $(delv_DEPENDENCIES) $(EXTRA_delv_DEPENDENCIES) + @rm -f delv$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(delv_OBJECTS) $(delv_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delv.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/delv.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/delv.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-binPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir doc-am doc-local dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-binPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/delv/delv.c b/bin/delv/delv.c new file mode 100644 index 0000000..e1e9e7f --- /dev/null +++ b/bin/delv/delv.c @@ -0,0 +1,1868 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +#define MAXNAME (DNS_NAME_MAXTEXT + 1) + +/* Variables used internally by delv. */ +char *progname; +static isc_mem_t *mctx = NULL; +static isc_log_t *lctx = NULL; + +/* Configurables */ +static char *server = NULL; +static const char *port = "53"; +static isc_sockaddr_t *srcaddr4 = NULL, *srcaddr6 = NULL; +static isc_sockaddr_t a4, a6; +static char *curqname = NULL, *qname = NULL; +static bool classset = false; +static dns_rdatatype_t qtype = dns_rdatatype_none; +static bool typeset = false; + +static unsigned int styleflags = 0; +static uint32_t splitwidth = 0xffffffff; +static bool showcomments = true, showdnssec = true, showtrust = true, + rrcomments = true, noclass = false, nocrypto = false, nottl = false, + multiline = false, short_form = false, print_unknown_format = false, + yaml = false; + +static bool resolve_trace = false, validator_trace = false, + message_trace = false; + +static bool use_ipv4 = true, use_ipv6 = true; + +static bool cdflag = false, no_sigs = false, root_validation = true; + +static bool use_tcp = false; + +static char *anchorfile = NULL; +static char *trust_anchor = NULL; +static int num_keys = 0; + +static dns_fixedname_t afn; +static dns_name_t *anchor_name = NULL; + +/* Default bind.keys contents */ +static char anchortext[] = TRUST_ANCHORS; + +/* + * Static function prototypes + */ +static isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict); + +static isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc); + +static void +usage(void) { + fprintf(stderr, + "Usage: delv [@server] {q-opt} {d-opt} [domain] [q-type] " + "[q-class]\n" + "Where: domain is in the Domain Name System\n" + " q-class is one of (in,hs,ch,...) [default: in]\n" + " q-type is one of " + "(a,any,mx,ns,soa,hinfo,axfr,txt,...) " + "[default:a]\n" + " q-opt is one of:\n" + " -4 (use IPv4 query " + "transport " + "only)\n" + " -6 (use IPv6 query " + "transport " + "only)\n" + " -a anchor-file (specify root trust " + "anchor)\n" + " -b address[#port] (bind to source " + "address/port)\n" + " -c class (option included for " + "compatibility;\n" + " -d level (set debugging level)\n" + " -h (print help and exit)\n" + " -i (disable DNSSEC " + "validation)\n" + " -m (enable memory usage " + "debugging)\n" + " -p port (specify port number)\n" + " -q name (specify query name)\n" + " -t type (specify query type)\n" + " only IN is supported)\n" + " -v (print version and " + "exit)\n" + " -x dot-notation (shortcut for reverse " + "lookups)\n" + " d-opt is of the form +keyword[=value], where " + "keyword " + "is:\n" + " +[no]all (Set or clear all " + "display " + "flags)\n" + " +[no]class (Control display of " + "class)\n" + " +[no]comments (Control display of " + "comment lines)\n" + " +[no]crypto (Control display of " + "cryptographic\n" + " fields in records)\n" + " +[no]dlv (Obsolete)\n" + " +[no]dnssec (Display DNSSEC " + "records)\n" + " +[no]mtrace (Trace messages " + "received)\n" + " +[no]multiline (Print records in an " + "expanded format)\n" + " +[no]root (DNSSEC validation trust " + "anchor)\n" + " +[no]rrcomments (Control display of " + "per-record " + "comments)\n" + " +[no]rtrace (Trace resolver " + "fetches)\n" + " +[no]short (Short form answer)\n" + " +[no]split=## (Split hex/base64 fields " + "into chunks)\n" + " +[no]tcp (TCP mode)\n" + " +[no]ttl (Control display of ttls " + "in records)\n" + " +[no]trust (Control display of " + "trust " + "level)\n" + " +[no]unknownformat (Print RDATA in RFC 3597 " + "\"unknown\" format)\n" + " +[no]vtrace (Trace validation " + "process)\n" + " +[no]yaml (Present the results as " + "YAML)\n"); + exit(1); +} + +noreturn static void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +static void +fatal(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + +static void +warn(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +static void +warn(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: warning: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + +static isc_logcategory_t categories[] = { { "delv", 0 }, { NULL, 0 } }; +#define LOGCATEGORY_DEFAULT (&categories[0]) +#define LOGMODULE_DEFAULT (&modules[0]) + +static isc_logmodule_t modules[] = { { "delv", 0 }, { NULL, 0 } }; + +static void +delv_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); + +static void +delv_log(int level, const char *fmt, ...) { + va_list ap; + char msgbuf[2048]; + + if (!isc_log_wouldlog(lctx, level)) { + return; + } + + va_start(ap, fmt); + + vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + isc_log_write(lctx, LOGCATEGORY_DEFAULT, LOGMODULE_DEFAULT, level, "%s", + msgbuf); + va_end(ap); +} + +static int loglevel = 0; + +static void +setup_logging(FILE *errout) { + isc_result_t result; + isc_logdestination_t destination; + isc_logconfig_t *logconfig = NULL; + + isc_log_create(mctx, &lctx, &logconfig); + isc_log_registercategories(lctx, categories); + isc_log_registermodules(lctx, modules); + isc_log_setcontext(lctx); + dns_log_init(lctx); + dns_log_setcontext(lctx); + cfg_log_init(lctx); + + destination.file.stream = errout; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, + ISC_LOG_DYNAMIC, &destination, + ISC_LOG_PRINTPREFIX); + + isc_log_setdebuglevel(lctx, loglevel); + isc_log_settag(logconfig, ";; "); + + result = isc_log_usechannel(logconfig, "stderr", + ISC_LOGCATEGORY_DEFAULT, NULL); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't attach to log channel 'stderr'"); + } + + if (resolve_trace && loglevel < 1) { + isc_log_createchannel(logconfig, "resolver", ISC_LOG_TOFILEDESC, + ISC_LOG_DEBUG(1), &destination, + ISC_LOG_PRINTPREFIX); + + result = isc_log_usechannel(logconfig, "resolver", + DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_RESOLVER); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't attach to log channel 'resolver'"); + } + } + + if (validator_trace && loglevel < 3) { + isc_log_createchannel(logconfig, "validator", + ISC_LOG_TOFILEDESC, ISC_LOG_DEBUG(3), + &destination, ISC_LOG_PRINTPREFIX); + + result = isc_log_usechannel(logconfig, "validator", + DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_VALIDATOR); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't attach to log channel 'validator'"); + } + } + + if (message_trace && loglevel < 10) { + isc_log_createchannel(logconfig, "messages", ISC_LOG_TOFILEDESC, + ISC_LOG_DEBUG(10), &destination, + ISC_LOG_PRINTPREFIX); + + result = isc_log_usechannel(logconfig, "messages", + DNS_LOGCATEGORY_RESOLVER, + DNS_LOGMODULE_PACKETS); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't attach to log channel 'messagse'"); + } + } +} + +static void +print_status(dns_rdataset_t *rdataset) { + char buf[1024] = { 0 }; + + REQUIRE(rdataset != NULL); + + if (!showtrust || !dns_rdataset_isassociated(rdataset)) { + return; + } + + buf[0] = '\0'; + + if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { + strlcat(buf, "negative response", sizeof(buf)); + strlcat(buf, (yaml ? "_" : ", "), sizeof(buf)); + } + + switch (rdataset->trust) { + case dns_trust_none: + strlcat(buf, "untrusted", sizeof(buf)); + break; + case dns_trust_pending_additional: + strlcat(buf, "signed additional data", sizeof(buf)); + if (!yaml) { + strlcat(buf, ", ", sizeof(buf)); + } + strlcat(buf, "pending validation", sizeof(buf)); + break; + case dns_trust_pending_answer: + strlcat(buf, "signed answer", sizeof(buf)); + if (!yaml) { + strlcat(buf, ", ", sizeof(buf)); + } + strlcat(buf, "pending validation", sizeof(buf)); + break; + case dns_trust_additional: + strlcat(buf, "unsigned additional data", sizeof(buf)); + break; + case dns_trust_glue: + strlcat(buf, "glue data", sizeof(buf)); + break; + case dns_trust_answer: + if (root_validation) { + strlcat(buf, "unsigned answer", sizeof(buf)); + } else { + strlcat(buf, "answer not validated", sizeof(buf)); + } + break; + case dns_trust_authauthority: + strlcat(buf, "authority data", sizeof(buf)); + break; + case dns_trust_authanswer: + strlcat(buf, "authoritative", sizeof(buf)); + break; + case dns_trust_secure: + strlcat(buf, "fully validated", sizeof(buf)); + break; + case dns_trust_ultimate: + strlcat(buf, "ultimate trust", sizeof(buf)); + break; + } + + if (yaml) { + char *p; + + /* Convert spaces to underscores for YAML */ + for (p = buf; p != NULL && *p != '\0'; p++) { + if (*p == ' ') { + *p = '_'; + } + } + + printf(" - %s:\n", buf); + } else { + printf("; %s\n", buf); + } +} + +static isc_result_t +printdata(dns_rdataset_t *rdataset, dns_name_t *owner, + dns_master_style_t *style) { + isc_result_t result = ISC_R_SUCCESS; + static dns_trust_t trust; + static bool first = true; + isc_buffer_t target; + isc_region_t r; + char *t = NULL; + int len = 2048; + + if (!dns_rdataset_isassociated(rdataset)) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(owner, namebuf, sizeof(namebuf)); + delv_log(ISC_LOG_DEBUG(4), "WARN: empty rdataset %s", namebuf); + return (ISC_R_SUCCESS); + } + + if (!showdnssec && rdataset->type == dns_rdatatype_rrsig) { + return (ISC_R_SUCCESS); + } + + if (first || rdataset->trust != trust) { + if (!first && showtrust && !short_form && !yaml) { + putchar('\n'); + } + print_status(rdataset); + trust = rdataset->trust; + first = false; + } + + do { + t = isc_mem_get(mctx, len); + + isc_buffer_init(&target, t, len); + if (short_form) { + dns_rdata_t rdata = DNS_RDATA_INIT; + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + if ((rdataset->attributes & + DNS_RDATASETATTR_NEGATIVE) != 0) + { + continue; + } + + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tofmttext( + &rdata, dns_rootname, styleflags, 0, + splitwidth, " ", &target); + if (result != ISC_R_SUCCESS) { + break; + } + + if (isc_buffer_availablelength(&target) < 1) { + result = ISC_R_NOSPACE; + break; + } + + isc_buffer_putstr(&target, "\n"); + + dns_rdata_reset(&rdata); + } + } else { + dns_indent_t indent = { " ", 2 }; + if (!yaml && (rdataset->attributes & + DNS_RDATASETATTR_NEGATIVE) != 0) + { + isc_buffer_putstr(&target, "; "); + } + result = dns_master_rdatasettotext( + owner, rdataset, style, yaml ? &indent : NULL, + &target); + } + + if (result == ISC_R_NOSPACE) { + isc_mem_put(mctx, t, len); + len += 1024; + } else if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } else { + CHECK(result); + } + } while (result == ISC_R_NOSPACE); + + isc_buffer_usedregion(&target, &r); + printf("%.*s", (int)r.length, (char *)r.base); + +cleanup: + if (t != NULL) { + isc_mem_put(mctx, t, len); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +setup_style(dns_master_style_t **stylep) { + isc_result_t result; + dns_master_style_t *style = NULL; + + REQUIRE(stylep != NULL && *stylep == NULL); + + styleflags |= DNS_STYLEFLAG_REL_OWNER; + if (yaml) { + styleflags |= DNS_STYLEFLAG_YAML; + } else { + if (showcomments) { + styleflags |= DNS_STYLEFLAG_COMMENT; + } + if (print_unknown_format) { + styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; + } + if (rrcomments) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + if (nottl) { + styleflags |= DNS_STYLEFLAG_NO_TTL; + } + if (noclass) { + styleflags |= DNS_STYLEFLAG_NO_CLASS; + } + if (nocrypto) { + styleflags |= DNS_STYLEFLAG_NOCRYPTO; + } + if (multiline) { + styleflags |= DNS_STYLEFLAG_MULTILINE; + styleflags |= DNS_STYLEFLAG_COMMENT; + } + } + + if (multiline || (nottl && noclass)) { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, + 32, 80, 8, splitwidth, mctx); + } else if (nottl || noclass) { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, + 40, 80, 8, splitwidth, mctx); + } else { + result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, + 48, 80, 8, splitwidth, mctx); + } + + if (result == ISC_R_SUCCESS) { + *stylep = style; + } + return (result); +} + +static isc_result_t +convert_name(dns_fixedname_t *fn, dns_name_t **name, const char *text) { + isc_result_t result; + isc_buffer_t b; + dns_name_t *n; + unsigned int len; + + REQUIRE(fn != NULL && name != NULL && text != NULL); + len = strlen(text); + + isc_buffer_constinit(&b, text, len); + isc_buffer_add(&b, len); + n = dns_fixedname_initname(fn); + + result = dns_name_fromtext(n, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "failed to convert QNAME %s: %s", text, + isc_result_totext(result)); + return (result); + } + + *name = n; + return (ISC_R_SUCCESS); +} + +static isc_result_t +key_fromconfig(const cfg_obj_t *key, dns_client_t *client) { + dns_rdata_dnskey_t dnskey; + dns_rdata_ds_t ds; + uint32_t rdata1, rdata2, rdata3; + const char *datastr = NULL, *keynamestr = NULL, *atstr = NULL; + unsigned char data[4096]; + isc_buffer_t databuf; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; + isc_region_t r; + dns_fixedname_t fkeyname; + dns_name_t *keyname; + isc_result_t result; + bool match_root = false; + enum { + INITIAL_KEY, + STATIC_KEY, + INITIAL_DS, + STATIC_DS, + TRUSTED + } anchortype; + const cfg_obj_t *obj; + + keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + CHECK(convert_name(&fkeyname, &keyname, keynamestr)); + + if (!root_validation) { + return (ISC_R_SUCCESS); + } + + if (anchor_name) { + match_root = dns_name_equal(keyname, anchor_name); + } + + if (!match_root) { + return (ISC_R_SUCCESS); + } + + if (!root_validation) { + return (ISC_R_SUCCESS); + } + + delv_log(ISC_LOG_DEBUG(3), "adding trust anchor %s", trust_anchor); + + /* if DNSKEY, flags; if DS, key tag */ + rdata1 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata1")); + + /* if DNSKEY, protocol; if DS, algorithm */ + rdata2 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata2")); + + /* if DNSKEY, algorithm; if DS, digest type */ + rdata3 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata3")); + + /* What type of trust anchor is this? */ + obj = cfg_tuple_get(key, "anchortype"); + if (cfg_obj_isvoid(obj)) { + /* + * "anchortype" is not defined, this must be a static-key + * configured with trusted-keys. + */ + anchortype = STATIC_KEY; + } else { + atstr = cfg_obj_asstring(obj); + if (strcasecmp(atstr, "static-key") == 0) { + anchortype = STATIC_KEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + anchortype = STATIC_DS; + } else if (strcasecmp(atstr, "initial-key") == 0) { + anchortype = INITIAL_KEY; + } else if (strcasecmp(atstr, "initial-ds") == 0) { + anchortype = INITIAL_DS; + } else { + delv_log(ISC_LOG_ERROR, + "key '%s': invalid initialization method '%s'", + keynamestr, atstr); + result = ISC_R_FAILURE; + goto cleanup; + } + } + + isc_buffer_init(&databuf, data, sizeof(data)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + if (rdata1 > 0xffff) { + CHECK(ISC_R_RANGE); + } + if (rdata2 > 0xff) { + CHECK(ISC_R_RANGE); + } + if (rdata3 > 0xff) { + CHECK(ISC_R_RANGE); + } + + switch (anchortype) { + case STATIC_KEY: + case INITIAL_KEY: + case TRUSTED: + dnskey.common.rdclass = dns_rdataclass_in; + dnskey.common.rdtype = dns_rdatatype_dnskey; + dnskey.mctx = NULL; + + ISC_LINK_INIT(&dnskey.common, link); + + dnskey.flags = (uint16_t)rdata1; + dnskey.protocol = (uint8_t)rdata2; + dnskey.algorithm = (uint8_t)rdata3; + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_base64_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + dnskey.datalen = r.length; + dnskey.data = r.base; + + CHECK(dns_rdata_fromstruct(NULL, dnskey.common.rdclass, + dnskey.common.rdtype, &dnskey, + &rrdatabuf)); + CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_dnskey, keyname, + &rrdatabuf)); + break; + case INITIAL_DS: + case STATIC_DS: + ds.common.rdclass = dns_rdataclass_in; + ds.common.rdtype = dns_rdatatype_ds; + ds.mctx = NULL; + + ISC_LINK_INIT(&ds.common, link); + + ds.key_tag = (uint16_t)rdata1; + ds.algorithm = (uint8_t)rdata2; + ds.digest_type = (uint8_t)rdata3; + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_hex_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + + switch (ds.digest_type) { + case DNS_DSDIGEST_SHA1: + if (r.length != ISC_SHA1_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA256: + if (r.length != ISC_SHA256_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA384: + if (r.length != ISC_SHA384_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + } + + ds.length = r.length; + ds.digest = r.base; + + CHECK(dns_rdata_fromstruct(NULL, ds.common.rdclass, + ds.common.rdtype, &ds, &rrdatabuf)); + CHECK(dns_client_addtrustedkey(client, dns_rdataclass_in, + dns_rdatatype_ds, keyname, + &rrdatabuf)); + } + + num_keys++; + +cleanup: + if (result == DST_R_NOCRYPTO) { + cfg_obj_log(key, lctx, ISC_LOG_ERROR, "no crypto support"); + } else if (result == DST_R_UNSUPPORTEDALG) { + cfg_obj_log(key, lctx, ISC_LOG_WARNING, + "skipping trusted key '%s': %s", keynamestr, + isc_result_totext(result)); + result = ISC_R_SUCCESS; + } else if (result != ISC_R_SUCCESS) { + cfg_obj_log(key, lctx, ISC_LOG_ERROR, + "failed to add trusted key '%s': %s", keynamestr, + isc_result_totext(result)); + result = ISC_R_FAILURE; + } + + return (result); +} + +static isc_result_t +load_keys(const cfg_obj_t *keys, dns_client_t *client) { + const cfg_listelt_t *elt, *elt2; + const cfg_obj_t *key, *keylist; + isc_result_t result = ISC_R_SUCCESS; + + for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + + for (elt2 = cfg_list_first(keylist); elt2 != NULL; + elt2 = cfg_list_next(elt2)) + { + key = cfg_listelt_value(elt2); + CHECK(key_fromconfig(key, client)); + } + } + +cleanup: + if (result == DST_R_NOCRYPTO) { + result = ISC_R_SUCCESS; + } + return (result); +} + +static isc_result_t +setup_dnsseckeys(dns_client_t *client) { + isc_result_t result; + cfg_parser_t *parser = NULL; + const cfg_obj_t *trusted_keys = NULL; + const cfg_obj_t *managed_keys = NULL; + const cfg_obj_t *trust_anchors = NULL; + cfg_obj_t *bindkeys = NULL; + const char *filename = anchorfile; + + if (!root_validation) { + return (ISC_R_SUCCESS); + } + + if (filename == NULL) { + filename = SYSCONFDIR "/bind.keys"; + } + + if (trust_anchor == NULL) { + trust_anchor = isc_mem_strdup(mctx, "."); + } + + if (trust_anchor != NULL) { + CHECK(convert_name(&afn, &anchor_name, trust_anchor)); + } + + CHECK(cfg_parser_create(mctx, dns_lctx, &parser)); + + if (access(filename, R_OK) != 0) { + if (anchorfile != NULL) { + fatal("Unable to read key file '%s'", anchorfile); + } + } else { + result = cfg_parse_file(parser, filename, &cfg_type_bindkeys, + &bindkeys); + if (result != ISC_R_SUCCESS) { + if (anchorfile != NULL) { + fatal("Unable to load keys from '%s'", + anchorfile); + } + } + } + + if (bindkeys == NULL) { + isc_buffer_t b; + + isc_buffer_init(&b, anchortext, sizeof(anchortext) - 1); + isc_buffer_add(&b, sizeof(anchortext) - 1); + cfg_parser_reset(parser); + result = cfg_parse_buffer(parser, &b, NULL, 0, + &cfg_type_bindkeys, 0, &bindkeys); + if (result != ISC_R_SUCCESS) { + fatal("Unable to parse built-in keys"); + } + } + + INSIST(bindkeys != NULL); + cfg_map_get(bindkeys, "trusted-keys", &trusted_keys); + cfg_map_get(bindkeys, "managed-keys", &managed_keys); + cfg_map_get(bindkeys, "trust-anchors", &trust_anchors); + + if (trusted_keys != NULL) { + CHECK(load_keys(trusted_keys, client)); + } + if (managed_keys != NULL) { + CHECK(load_keys(managed_keys, client)); + } + if (trust_anchors != NULL) { + CHECK(load_keys(trust_anchors, client)); + } + result = ISC_R_SUCCESS; + + if (num_keys == 0) { + fatal("No trusted keys were loaded"); + } + +cleanup: + if (bindkeys != NULL) { + cfg_obj_destroy(parser, &bindkeys); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "setup_dnsseckeys: %s", + isc_result_totext(result)); + } + return (result); +} + +static isc_result_t +addserver(dns_client_t *client) { + struct addrinfo hints, *res, *cur; + int gaierror; + struct in_addr in4; + struct in6_addr in6; + isc_sockaddr_t *sa; + isc_sockaddrlist_t servers; + uint32_t destport; + isc_result_t result; + dns_name_t *name = NULL; + + result = parse_uint(&destport, port, 0xffff, "port"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + + ISC_LIST_INIT(servers); + + if (inet_pton(AF_INET, server, &in4) == 1) { + if (!use_ipv4) { + fatal("Use of IPv4 disabled by -6"); + } + sa = isc_mem_get(mctx, sizeof(*sa)); + ISC_LINK_INIT(sa, link); + isc_sockaddr_fromin(sa, &in4, destport); + ISC_LIST_APPEND(servers, sa, link); + } else if (inet_pton(AF_INET6, server, &in6) == 1) { + if (!use_ipv6) { + fatal("Use of IPv6 disabled by -4"); + } + sa = isc_mem_get(mctx, sizeof(*sa)); + ISC_LINK_INIT(sa, link); + isc_sockaddr_fromin6(sa, &in6, destport); + ISC_LIST_APPEND(servers, sa, link); + } else { + memset(&hints, 0, sizeof(hints)); + if (!use_ipv6) { + hints.ai_family = AF_INET; + } else if (!use_ipv4) { + hints.ai_family = AF_INET6; + } else { + hints.ai_family = AF_UNSPEC; + } + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + gaierror = getaddrinfo(server, port, &hints, &res); + if (gaierror != 0) { + delv_log(ISC_LOG_ERROR, "getaddrinfo failed: %s", + gai_strerror(gaierror)); + return (ISC_R_FAILURE); + } + + result = ISC_R_SUCCESS; + for (cur = res; cur != NULL; cur = cur->ai_next) { + if (cur->ai_family != AF_INET && + cur->ai_family != AF_INET6) + { + continue; + } + sa = isc_mem_get(mctx, sizeof(*sa)); + memset(sa, 0, sizeof(*sa)); + ISC_LINK_INIT(sa, link); + memmove(&sa->type, cur->ai_addr, cur->ai_addrlen); + sa->length = (unsigned int)cur->ai_addrlen; + ISC_LIST_APPEND(servers, sa, link); + } + freeaddrinfo(res); + CHECK(result); + } + + CHECK(dns_client_setservers(client, dns_rdataclass_in, name, &servers)); + +cleanup: + while (!ISC_LIST_EMPTY(servers)) { + sa = ISC_LIST_HEAD(servers); + ISC_LIST_UNLINK(servers, sa, link); + isc_mem_put(mctx, sa, sizeof(*sa)); + } + + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "addserver: %s", + isc_result_totext(result)); + } + + return (result); +} + +static isc_result_t +findserver(dns_client_t *client) { + isc_result_t result; + irs_resconf_t *resconf = NULL; + isc_sockaddrlist_t *nameservers; + isc_sockaddr_t *sa, *next; + uint32_t destport; + + result = parse_uint(&destport, port, 0xffff, "port"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + + result = irs_resconf_load(mctx, "/etc/resolv.conf", &resconf); + if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { + delv_log(ISC_LOG_ERROR, "irs_resconf_load: %s", + isc_result_totext(result)); + goto cleanup; + } + + /* Get nameservers from resolv.conf */ + nameservers = irs_resconf_getnameservers(resconf); + for (sa = ISC_LIST_HEAD(*nameservers); sa != NULL; sa = next) { + next = ISC_LIST_NEXT(sa, link); + + /* Set destination port */ + if (sa->type.sa.sa_family == AF_INET && use_ipv4) { + sa->type.sin.sin_port = htons(destport); + continue; + } + if (sa->type.sa.sa_family == AF_INET6 && use_ipv6) { + sa->type.sin6.sin6_port = htons(destport); + continue; + } + + /* Incompatible protocol family */ + ISC_LIST_UNLINK(*nameservers, sa, link); + isc_mem_put(mctx, sa, sizeof(*sa)); + } + + /* None found, use localhost */ + if (ISC_LIST_EMPTY(*nameservers)) { + if (use_ipv4) { + struct in_addr localhost; + localhost.s_addr = htonl(INADDR_LOOPBACK); + sa = isc_mem_get(mctx, sizeof(*sa)); + isc_sockaddr_fromin(sa, &localhost, destport); + + ISC_LINK_INIT(sa, link); + ISC_LIST_APPEND(*nameservers, sa, link); + } + + if (use_ipv6) { + sa = isc_mem_get(mctx, sizeof(*sa)); + isc_sockaddr_fromin6(sa, &in6addr_loopback, destport); + + ISC_LINK_INIT(sa, link); + ISC_LIST_APPEND(*nameservers, sa, link); + } + } + + result = dns_client_setservers(client, dns_rdataclass_in, NULL, + nameservers); + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "dns_client_setservers: %s", + isc_result_totext(result)); + } + +cleanup: + if (resconf != NULL) { + irs_resconf_destroy(&resconf); + } + return (result); +} + +static isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { + uint32_t n; + isc_result_t result = isc_parse_uint32(&n, value, 10); + if (result == ISC_R_SUCCESS && n > max) { + result = ISC_R_RANGE; + } + if (result != ISC_R_SUCCESS) { + printf("invalid %s '%s': %s\n", desc, value, + isc_result_totext(result)); + return (result); + } + *uip = n; + return (ISC_R_SUCCESS); +} + +static void +plus_option(char *option) { + isc_result_t result; + char *cmd, *value, *last = NULL; + bool state = true; + + INSIST(option != NULL); + + cmd = strtok_r(option, "=", &last); + if (cmd == NULL) { + printf(";; Invalid option %s\n", option); + return; + } + if (strncasecmp(cmd, "no", 2) == 0) { + cmd += 2; + state = false; + } + + value = strtok_r(NULL, "\0", &last); + +#define FULLCHECK(A) \ + do { \ + size_t _l = strlen(cmd); \ + if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ + goto invalid_option; \ + } while (0) + + switch (cmd[0]) { + case 'a': /* all */ + FULLCHECK("all"); + showcomments = state; + rrcomments = state; + showtrust = state; + break; + case 'c': + switch (cmd[1]) { + case 'd': /* cdflag */ + FULLCHECK("cdflag"); + cdflag = state; + break; + case 'l': /* class */ + FULLCHECK("class"); + noclass = !state; + break; + case 'o': /* comments */ + FULLCHECK("comments"); + showcomments = state; + break; + case 'r': /* crypto */ + FULLCHECK("crypto"); + nocrypto = !state; + break; + default: + goto invalid_option; + } + break; + case 'd': + switch (cmd[1]) { + case 'l': /* dlv */ + FULLCHECK("dlv"); + if (state) { + fprintf(stderr, "Invalid option: " + "+dlv is obsolete\n"); + exit(1); + } + break; + case 'n': /* dnssec */ + FULLCHECK("dnssec"); + showdnssec = state; + break; + default: + goto invalid_option; + } + break; + case 'm': + switch (cmd[1]) { + case 't': /* mtrace */ + message_trace = state; + if (state) { + resolve_trace = state; + } + break; + case 'u': /* multiline */ + FULLCHECK("multiline"); + multiline = state; + break; + default: + goto invalid_option; + } + break; + case 'r': + switch (cmd[1]) { + case 'o': /* root */ + FULLCHECK("root"); + if (state && no_sigs) { + break; + } + root_validation = state; + if (value != NULL) { + trust_anchor = isc_mem_strdup(mctx, value); + } + break; + case 'r': /* rrcomments */ + FULLCHECK("rrcomments"); + rrcomments = state; + break; + case 't': /* rtrace */ + FULLCHECK("rtrace"); + resolve_trace = state; + break; + default: + goto invalid_option; + } + break; + case 's': + switch (cmd[1]) { + case 'h': /* short */ + FULLCHECK("short"); + short_form = state; + if (short_form) { + multiline = false; + showcomments = false; + showtrust = false; + showdnssec = false; + } + break; + case 'p': /* split */ + FULLCHECK("split"); + if (value != NULL && !state) { + goto invalid_option; + } + if (!state) { + splitwidth = 0; + break; + } else if (value == NULL) { + break; + } + + result = parse_uint(&splitwidth, value, 1023, "split"); + if (splitwidth % 4 != 0) { + splitwidth = ((splitwidth + 3) / 4) * 4; + warn("split must be a multiple of 4; " + "adjusting to %d", + splitwidth); + } + /* + * There is an adjustment done in the + * totext_() functions which causes + * splitwidth to shrink. This is okay when we're + * using the default width but incorrect in this + * case, so we correct for it + */ + if (splitwidth) { + splitwidth += 3; + } + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse split"); + } + break; + default: + goto invalid_option; + } + break; + case 'u': + FULLCHECK("unknownformat"); + print_unknown_format = state; + break; + case 't': + switch (cmd[1]) { + case 'c': /* tcp */ + FULLCHECK("tcp"); + use_tcp = state; + break; + case 'r': /* trust */ + FULLCHECK("trust"); + showtrust = state; + break; + case 't': /* ttl */ + FULLCHECK("ttl"); + nottl = !state; + break; + default: + goto invalid_option; + } + break; + case 'v': /* vtrace */ + FULLCHECK("vtrace"); + validator_trace = state; + if (state) { + resolve_trace = state; + } + break; + case 'y': /* yaml */ + FULLCHECK("yaml"); + yaml = state; + if (state) { + rrcomments = false; + } + break; + default: + invalid_option: + /* + * We can also add a "need_value:" case here if we ever + * add a plus-option that requires a specified value + */ + fprintf(stderr, "Invalid option: +%s\n", option); + usage(); + } + return; +} + +/* + * options: "46a:b:c:d:himp:q:t:vx:"; + */ +static const char *single_dash_opts = "46himv"; +static const char *dash_opts = "46abcdhimpqtvx"; + +static bool +dash_option(char *option, char *next, bool *open_type_class) { + char opt, *value; + isc_result_t result; + bool value_from_next; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + char textname[MAXNAME]; + struct in_addr in4; + struct in6_addr in6; + in_port_t srcport; + uint32_t num; + char *hash; + + while (strpbrk(option, single_dash_opts) == &option[0]) { + /* + * Since the -[46himv] options do not take an argument, + * account for them (in any number and/or combination) + * if they appear as the first character(s) of a q-opt. + */ + opt = option[0]; + switch (opt) { + case '4': + if (isc_net_probeipv4() != ISC_R_SUCCESS) { + fatal("IPv4 networking not available"); + } + if (use_ipv6) { + isc_net_disableipv6(); + use_ipv6 = false; + } + break; + case '6': + if (isc_net_probeipv6() != ISC_R_SUCCESS) { + fatal("IPv6 networking not available"); + } + if (use_ipv4) { + isc_net_disableipv4(); + use_ipv4 = false; + } + break; + case 'h': + usage(); + exit(0); + case 'i': + no_sigs = true; + root_validation = false; + break; + case 'm': + /* handled in preparse_args() */ + break; + case 'v': + fprintf(stderr, "delv %s\n", PACKAGE_VERSION); + exit(0); + default: + UNREACHABLE(); + } + if (strlen(option) > 1U) { + option = &option[1]; + } else { + return (false); + } + } + opt = option[0]; + if (strlen(option) > 1U) { + value_from_next = false; + value = &option[1]; + } else { + value_from_next = true; + value = next; + } + if (value == NULL) { + goto invalid_option; + } + switch (opt) { + case 'a': + anchorfile = isc_mem_strdup(mctx, value); + return (value_from_next); + case 'b': + hash = strchr(value, '#'); + if (hash != NULL) { + result = parse_uint(&num, hash + 1, 0xffff, "port"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + srcport = num; + *hash = '\0'; + } else { + srcport = 0; + } + + if (inet_pton(AF_INET, value, &in4) == 1) { + if (srcaddr4 != NULL) { + fatal("Only one local address per family " + "can be specified\n"); + } + isc_sockaddr_fromin(&a4, &in4, srcport); + srcaddr4 = &a4; + } else if (inet_pton(AF_INET6, value, &in6) == 1) { + if (srcaddr6 != NULL) { + fatal("Only one local address per family " + "can be specified\n"); + } + isc_sockaddr_fromin6(&a6, &in6, srcport); + srcaddr6 = &a6; + } else { + if (hash != NULL) { + *hash = '#'; + } + fatal("Invalid address %s", value); + } + if (hash != NULL) { + *hash = '#'; + } + return (value_from_next); + case 'c': + if (classset) { + warn("extra query class"); + } + + *open_type_class = false; + tr.base = value; + tr.length = strlen(value); + result = dns_rdataclass_fromtext(&rdclass, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + classset = true; + } else if (rdclass != dns_rdataclass_in) { + warn("ignoring non-IN query class"); + } else { + warn("ignoring invalid class"); + } + return (value_from_next); + case 'd': + result = parse_uint(&num, value, 99, "debug level"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse debug level"); + } + loglevel = num; + return (value_from_next); + case 'p': + port = value; + return (value_from_next); + case 'q': + if (curqname != NULL) { + warn("extra query name"); + isc_mem_free(mctx, curqname); + } + curqname = isc_mem_strdup(mctx, value); + return (value_from_next); + case 't': + *open_type_class = false; + tr.base = value; + tr.length = strlen(value); + result = dns_rdatatype_fromtext(&rdtype, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + if (typeset) { + warn("extra query type"); + } + if (rdtype == dns_rdatatype_ixfr || + rdtype == dns_rdatatype_axfr) + { + fatal("Transfer not supported"); + } + qtype = rdtype; + typeset = true; + } else { + warn("ignoring invalid type"); + } + return (value_from_next); + case 'x': + result = get_reverse(textname, sizeof(textname), value, false); + if (result == ISC_R_SUCCESS) { + if (curqname != NULL) { + isc_mem_free(mctx, curqname); + warn("extra query name"); + } + curqname = isc_mem_strdup(mctx, textname); + if (typeset) { + warn("extra query type"); + } + qtype = dns_rdatatype_ptr; + typeset = true; + } else { + fprintf(stderr, "Invalid IP address %s\n", value); + exit(1); + } + return (value_from_next); + invalid_option: + default: + fprintf(stderr, "Invalid option: -%s\n", option); + usage(); + } + UNREACHABLE(); + return (false); +} + +/* + * Check for -m first to determine whether to enable + * memory debugging when setting up the memory context. + */ +static void +preparse_args(int argc, char **argv) { + bool ipv4only = false, ipv6only = false; + char *option; + + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') { + continue; + } + + option = &argv[0][1]; + while (strpbrk(option, single_dash_opts) == &option[0]) { + switch (option[0]) { + case 'm': + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + } + option = &option[1]; + } + + if (strlen(option) == 0U) { + continue; + } + + /* Look for dash value option. */ + if (strpbrk(option, dash_opts) != &option[0] || + strlen(option) > 1U) + { + /* Error or value in option. */ + continue; + } + + /* Dash value is next argument so we need to skip it. */ + argc--; + argv++; + + /* Handle missing argument */ + if (argc == 0) { + break; + } + } +} + +/* + * Argument parsing is based on dig, but simplified: only one + * QNAME/QCLASS/QTYPE tuple can be specified, and options have + * been removed that aren't applicable to delv. The interface + * should be familiar to dig users, however. + */ +static void +parse_args(int argc, char **argv) { + isc_result_t result; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + bool open_type_class = true; + + for (; argc > 0; argc--, argv++) { + if (argv[0][0] == '@') { + server = &argv[0][1]; + } else if (argv[0][0] == '+') { + plus_option(&argv[0][1]); + } else if (argv[0][0] == '-') { + if (argc <= 1) { + if (dash_option(&argv[0][1], NULL, + &open_type_class)) + { + argc--; + argv++; + } + } else { + if (dash_option(&argv[0][1], argv[1], + &open_type_class)) + { + argc--; + argv++; + } + } + } else { + /* + * Anything which isn't an option + */ + if (open_type_class) { + tr.base = argv[0]; + tr.length = strlen(argv[0]); + result = dns_rdatatype_fromtext( + &rdtype, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + if (typeset) { + warn("extra query type"); + } + if (rdtype == dns_rdatatype_ixfr || + rdtype == dns_rdatatype_axfr) + { + fatal("Transfer not supported"); + } + qtype = rdtype; + typeset = true; + continue; + } + result = dns_rdataclass_fromtext( + &rdclass, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + if (classset) { + warn("extra query class"); + } else if (rdclass != dns_rdataclass_in) + { + warn("ignoring non-IN " + "query class"); + } + continue; + } + } + + if (curqname == NULL) { + curqname = isc_mem_strdup(mctx, argv[0]); + } + } + } + + /* + * If no qname or qtype specified, search for root/NS + * If no qtype specified, use A + */ + if (!typeset) { + qtype = dns_rdatatype_a; + } + + if (curqname == NULL) { + qname = isc_mem_strdup(mctx, "."); + + if (!typeset) { + qtype = dns_rdatatype_ns; + } + } else { + qname = curqname; + } +} + +static isc_result_t +append_str(const char *text, int len, char **p, char *end) { + if (len > end - *p) { + return (ISC_R_NOSPACE); + } + memmove(*p, text, len); + *p += len; + return (ISC_R_SUCCESS); +} + +static isc_result_t +reverse_octets(const char *in, char **p, char *end) { + char *dot = strchr(in, '.'); + int len; + if (dot != NULL) { + isc_result_t result; + result = reverse_octets(dot + 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = append_str(".", 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + len = (int)(dot - in); + } else { + len = strlen(in); + } + return (append_str(in, len, p, end)); +} + +static isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict) { + int r; + isc_result_t result; + isc_netaddr_t addr; + + addr.family = AF_INET6; + r = inet_pton(AF_INET6, value, &addr.type.in6); + if (r > 0) { + /* This is a valid IPv6 address. */ + dns_fixedname_t fname; + dns_name_t *name; + unsigned int options = 0; + + name = dns_fixedname_initname(&fname); + result = dns_byaddr_createptrname(&addr, options, name); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_name_format(name, reverse, (unsigned int)len); + return (ISC_R_SUCCESS); + } else { + /* + * Not a valid IPv6 address. Assume IPv4. + * If 'strict' is not set, construct the + * in-addr.arpa name by blindly reversing + * octets whether or not they look like integers, + * so that this can be used for RFC2317 names + * and such. + */ + char *p = reverse; + char *end = reverse + len; + if (strict && inet_pton(AF_INET, value, &addr.type.in) != 1) { + return (DNS_R_BADDOTTEDQUAD); + } + result = reverse_octets(value, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = append_str(".in-addr.arpa.", 15, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + return (ISC_R_SUCCESS); + } +} + +int +main(int argc, char *argv[]) { + dns_client_t *client = NULL; + isc_result_t result; + dns_fixedname_t qfn; + dns_name_t *query_name, *response_name; + char namestr[DNS_NAME_FORMATSIZE]; + dns_rdataset_t *rdataset; + dns_namelist_t namelist; + unsigned int resopt; + isc_appctx_t *actx = NULL; + isc_nm_t *netmgr = NULL; + isc_taskmgr_t *taskmgr = NULL; + isc_timermgr_t *timermgr = NULL; + dns_master_style_t *style = NULL; + struct sigaction sa; + + progname = argv[0]; + preparse_args(argc, argv); + + argc--; + argv++; + + isc_mem_create(&mctx); + + result = dst_lib_init(mctx, NULL); + if (result != ISC_R_SUCCESS) { + fatal("dst_lib_init failed: %d", result); + } + + CHECK(isc_appctx_create(mctx, &actx)); + + isc_managers_create(mctx, 1, 0, &netmgr, &taskmgr, &timermgr); + + parse_args(argc, argv); + + CHECK(setup_style(&style)); + + setup_logging(stderr); + + CHECK(isc_app_ctxstart(actx)); + + /* Unblock SIGINT if it's been blocked by isc_app_ctxstart() */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigfillset(&sa.sa_mask) != 0 || sigaction(SIGINT, &sa, NULL) < 0) { + fatal("Couldn't set up signal handler"); + } + + /* Create client */ + result = dns_client_create(mctx, actx, taskmgr, netmgr, timermgr, 0, + &client, srcaddr4, srcaddr6); + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "dns_client_create: %s", + isc_result_totext(result)); + goto cleanup; + } + + /* Set the nameserver */ + if (server != NULL) { + addserver(client); + } else { + findserver(client); + } + + CHECK(setup_dnsseckeys(client)); + + /* Construct QNAME */ + CHECK(convert_name(&qfn, &query_name, qname)); + + /* Set up resolution options */ + resopt = DNS_CLIENTRESOPT_NOCDFLAG; + if (no_sigs) { + resopt |= DNS_CLIENTRESOPT_NODNSSEC; + } + if (!root_validation) { + resopt |= DNS_CLIENTRESOPT_NOVALIDATE; + } + if (cdflag) { + resopt &= ~DNS_CLIENTRESOPT_NOCDFLAG; + } + if (use_tcp) { + resopt |= DNS_CLIENTRESOPT_TCP; + } + + /* Perform resolution */ + ISC_LIST_INIT(namelist); + result = dns_client_resolve(client, query_name, dns_rdataclass_in, + qtype, resopt, &namelist); + if (result != ISC_R_SUCCESS && !yaml) { + delv_log(ISC_LOG_ERROR, "resolution failed: %s", + isc_result_totext(result)); + } + + if (yaml) { + printf("type: DELV_RESULT\n"); + dns_name_format(query_name, namestr, sizeof(namestr)); + printf("query_name: %s\n", namestr); + printf("status: %s\n", isc_result_totext(result)); + printf("records:\n"); + } + + for (response_name = ISC_LIST_HEAD(namelist); response_name != NULL; + response_name = ISC_LIST_NEXT(response_name, link)) + { + for (rdataset = ISC_LIST_HEAD(response_name->list); + rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) + { + result = printdata(rdataset, response_name, style); + if (result != ISC_R_SUCCESS) { + delv_log(ISC_LOG_ERROR, "print data failed"); + } + } + } + + dns_client_freeresanswer(client, &namelist); + +cleanup: + if (trust_anchor != NULL) { + isc_mem_free(mctx, trust_anchor); + } + if (anchorfile != NULL) { + isc_mem_free(mctx, anchorfile); + } + if (qname != NULL) { + isc_mem_free(mctx, qname); + } + if (style != NULL) { + dns_master_styledestroy(&style, mctx); + } + if (client != NULL) { + dns_client_detach(&client); + } + + isc_managers_destroy(&netmgr, &taskmgr, &timermgr); + + if (actx != NULL) { + isc_appctx_destroy(&actx); + } + if (lctx != NULL) { + isc_log_destroy(&lctx); + } + isc_mem_detach(&mctx); + + dst_lib_destroy(); + + return (0); +} diff --git a/bin/delv/delv.rst b/bin/delv/delv.rst new file mode 100644 index 0000000..bf6cce1 --- /dev/null +++ b/bin/delv/delv.rst @@ -0,0 +1,364 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: delv +.. program:: delv +.. _man_delv: + +delv - DNS lookup and validation utility +---------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`delv` [@server] [ [**-4**] | [**-6**] ] [**-a** anchor-file] [**-b** address] [**-c** class] [**-d** level] [**-i**] [**-m**] [**-p** port#] [**-q** name] [**-t** type] [**-x** addr] [name] [type] [class] [queryopt...] + +:program:`delv` [**-h**] + +:program:`delv` [**-v**] + +:program:`delv` [queryopt...] [query...] + +Description +~~~~~~~~~~~ + +:program:`delv` is a tool for sending DNS queries and validating the results, +using the same internal resolver and validator logic as :iscman:`named`. + +:program:`delv` sends to a specified name server all queries needed to +fetch and validate the requested data; this includes the original +requested query, subsequent queries to follow CNAME or DNAME chains, +queries for DNSKEY, and DS records to establish a chain of trust for +DNSSEC validation. It does not perform iterative resolution, but +simulates the behavior of a name server configured for DNSSEC validating +and forwarding. + +By default, responses are validated using the built-in DNSSEC trust anchor +for the root zone ("."). Records returned by :program:`delv` are either fully +validated or were not signed. If validation fails, an explanation of the +failure is included in the output; the validation process can be traced +in detail. Because :program:`delv` does not rely on an external server to carry +out validation, it can be used to check the validity of DNS responses in +environments where local name servers may not be trustworthy. + +Unless it is told to query a specific name server, :program:`delv` tries +each of the servers listed in ``/etc/resolv.conf``. If no usable server +addresses are found, :program:`delv` sends queries to the localhost +addresses (127.0.0.1 for IPv4, ::1 for IPv6). + +When no command-line arguments or options are given, :program:`delv` +performs an NS query for "." (the root zone). + +Simple Usage +~~~~~~~~~~~~ + +A typical invocation of :program:`delv` looks like: + +:: + + delv @server name type + +where: + +.. option:: server + + is the name or IP address of the name server to query. This can be an + IPv4 address in dotted-decimal notation or an IPv6 address in + colon-delimited notation. When the supplied ``server`` argument is a + hostname, :program:`delv` resolves that name before querying that name + server (note, however, that this initial lookup is *not* validated by + DNSSEC). + + If no ``server`` argument is provided, :program:`delv` consults + ``/etc/resolv.conf``; if an address is found there, it queries the + name server at that address. If either of the :option:`-4` or :option:`-6` + options is in use, then only addresses for the corresponding + transport are tried. If no usable addresses are found, :program:`delv` + sends queries to the localhost addresses (127.0.0.1 for IPv4, ::1 + for IPv6). + +.. option:: name + + is the domain name to be looked up. + +.. option:: type + + indicates what type of query is required - ANY, A, MX, etc. + ``type`` can be any valid query type. If no ``type`` argument is + supplied, :program:`delv` performs a lookup for an A record. + +Options +~~~~~~~ + +.. option:: -a anchor-file + + This option specifies a file from which to read DNSSEC trust anchors. The default + is |bind_keys|, which is included with BIND 9 and contains one + or more trust anchors for the root zone ("."). + + Keys that do not match the root zone name are ignored. An alternate + key name can be specified using the :option:`+root` option. + + Note: When reading the trust anchor file, :program:`delv` treats ``trust-anchors``, + ``initial-key``, and ``static-key`` identically. That is, for a managed key, + it is the *initial* key that is trusted; :rfc:`5011` key management is not + supported. :program:`delv` does not consult the managed-keys database maintained by + :iscman:`named`, which means that if either of the keys in |bind_keys| is + revoked and rolled over, |bind_keys| must be updated to + use DNSSEC validation in :program:`delv`. + +.. option:: -b address + + This option sets the source IP address of the query to ``address``. This must be + a valid address on one of the host's network interfaces, or ``0.0.0.0``, + or ``::``. An optional source port may be specified by appending + ``#`` + +.. option:: -c class + + This option sets the query class for the requested data. Currently, only class + "IN" is supported in :program:`delv` and any other value is ignored. + +.. option:: -d level + + This option sets the systemwide debug level to ``level``. The allowed range is + from 0 to 99. The default is 0 (no debugging). Debugging traces from + :program:`delv` become more verbose as the debug level increases. See the + :option:`+mtrace`, :option:`+rtrace`, and :option:`+vtrace` options below for + additional debugging details. + +.. option:: -h + + This option displays the :program:`delv` help usage output and exits. + +.. option:: -i + + This option sets insecure mode, which disables internal DNSSEC validation. (Note, + however, that this does not set the CD bit on upstream queries. If the + server being queried is performing DNSSEC validation, then it does + not return invalid data; this can cause :program:`delv` to time out. When it + is necessary to examine invalid data to debug a DNSSEC problem, use + :option:`dig +cd`.) + +.. option:: -m + + This option enables memory usage debugging. + +.. option:: -p port# + + This option specifies a destination port to use for queries, instead of the + standard DNS port number 53. This option is used with a name + server that has been configured to listen for queries on a + non-standard port number. + +.. option:: -q name + + This option sets the query name to ``name``. While the query name can be + specified without using the :option:`-q` option, it is sometimes necessary to + disambiguate names from types or classes (for example, when looking + up the name "ns", which could be misinterpreted as the type NS, or + "ch", which could be misinterpreted as class CH). + +.. option:: -t type + + This option sets the query type to ``type``, which can be any valid query type + supported in BIND 9 except for zone transfer types AXFR and IXFR. As + with :option:`-q`, this is useful to distinguish query-name types or classes + when they are ambiguous. It is sometimes necessary to disambiguate + names from types. + + The default query type is "A", unless the :option:`-x` option is supplied + to indicate a reverse lookup, in which case it is "PTR". + +.. option:: -v + + This option prints the :program:`delv` version and exits. + +.. option:: -x addr + + This option performs a reverse lookup, mapping an address to a name. ``addr`` + is an IPv4 address in dotted-decimal notation, or a colon-delimited + IPv6 address. When :option:`-x` is used, there is no need to provide the + ``name`` or ``type`` arguments; :program:`delv` automatically performs a + lookup for a name like ``11.12.13.10.in-addr.arpa`` and sets the + query type to PTR. IPv6 addresses are looked up using nibble format + under the IP6.ARPA domain. + +.. option:: -4 + + This option forces :program:`delv` to only use IPv4. + +.. option:: -6 + + This option forces :program:`delv` to only use IPv6. + +Query Options +~~~~~~~~~~~~~ + +:program:`delv` provides a number of query options which affect the way results +are displayed, and in some cases the way lookups are performed. + +Each query option is identified by a keyword preceded by a plus sign +(``+``). Some keywords set or reset an option. These may be preceded by +the string ``no`` to negate the meaning of that keyword. Other keywords +assign values to options like the timeout interval. They have the form +``+keyword=value``. The query options are: + +.. option:: +cdflag, +nocdflag + + This option controls whether to set the CD (checking disabled) bit in queries + sent by :program:`delv`. This may be useful when troubleshooting DNSSEC + problems from behind a validating resolver. A validating resolver + blocks invalid responses, making it difficult to retrieve them + for analysis. Setting the CD flag on queries causes the resolver + to return invalid responses, which :program:`delv` can then validate + internally and report the errors in detail. + +.. option:: +class, +noclass + + This option controls whether to display the CLASS when printing a record. The + default is to display the CLASS. + +.. option:: +ttl, +nottl + + This option controls whether to display the TTL when printing a record. The + default is to display the TTL. + +.. option:: +rtrace, +nortrace + + This option toggles resolver fetch logging. This reports the name and type of each + query sent by :program:`delv` in the process of carrying out the resolution + and validation process, including the original query + and all subsequent queries to follow CNAMEs and to establish a chain + of trust for DNSSEC validation. + + This is equivalent to setting the debug level to 1 in the "resolver" + logging category. Setting the systemwide debug level to 1 using the + :option:`-d` option produces the same output, but affects other + logging categories as well. + +.. option:: +mtrace, +nomtrace + + This option toggles message logging. This produces a detailed dump of the + responses received by :program:`delv` in the process of carrying out the + resolution and validation process. + + This is equivalent to setting the debug level to 10 for the "packets" + module of the "resolver" logging category. Setting the systemwide + debug level to 10 using the :option:`-d` option produces the same + output, but affects other logging categories as well. + +.. option:: +vtrace, +novtrace + + This option toggles validation logging. This shows the internal process of the + validator as it determines whether an answer is validly signed, + unsigned, or invalid. + + This is equivalent to setting the debug level to 3 for the + "validator" module of the "dnssec" logging category. Setting the + systemwide debug level to 3 using the :option:`-d` option produces the + same output, but affects other logging categories as well. + +.. option:: +short, +noshort + + This option toggles between verbose and terse answers. The default is to print the answer in a + verbose form. + +.. option:: +comments, +nocomments + + This option toggles the display of comment lines in the output. The default is to + print comments. + +.. option:: +rrcomments, +norrcomments + + This option toggles the display of per-record comments in the output (for example, + human-readable key information about DNSKEY records). The default is + to print per-record comments. + +.. option:: +crypto, +nocrypto + + This option toggles the display of cryptographic fields in DNSSEC records. The + contents of these fields are unnecessary to debug most DNSSEC + validation failures and removing them makes it easier to see the + common failures. The default is to display the fields. When omitted, + they are replaced by the string ``[omitted]`` or, in the DNSKEY case, the + key ID is displayed as the replacement, e.g. ``[ key id = value ]``. + +.. option:: +trust, +notrust + + This option controls whether to display the trust level when printing a record. + The default is to display the trust level. + +.. option:: +split[=W], +nosplit + + This option splits long hex- or base64-formatted fields in resource records into + chunks of ``W`` characters (where ``W`` is rounded up to the nearest + multiple of 4). ``+nosplit`` or ``+split=0`` causes fields not to be + split at all. The default is 56 characters, or 44 characters when + multiline mode is active. + +.. option:: +all, +noall + + This option sets or clears the display options :option:`+comments`, + :option:`+rrcomments`, and :option:`+trust` as a group. + +.. option:: +multiline, +nomultiline + + This option prints long records (such as RRSIG, DNSKEY, and SOA records) in a + verbose multi-line format with human-readable comments. The default + is to print each record on a single line, to facilitate machine + parsing of the :program:`delv` output. + +.. option:: +dnssec, +nodnssec + + This option indicates whether to display RRSIG records in the :program:`delv` output. + The default is to do so. Note that (unlike in :iscman:`dig`) this does + *not* control whether to request DNSSEC records or to + validate them. DNSSEC records are always requested, and validation + always occurs unless suppressed by the use of :option:`-i` or + :option:`+noroot`. + +.. option:: +root[=ROOT], +noroot + + This option indicates whether to perform conventional DNSSEC validation, and if so, + specifies the name of a trust anchor. The default is to validate using a + trust anchor of "." (the root zone), for which there is a built-in key. If + specifying a different trust anchor, then :option:`-a` must be used to specify a + file containing the key. + +.. option:: +tcp, +notcp + + This option controls whether to use TCP when sending queries. The default is to + use UDP unless a truncated response has been received. + +.. option:: +unknownformat, +nounknownformat + + This option prints all RDATA in unknown RR-type presentation format (:rfc:`3597`). + The default is to print RDATA for known types in the type's + presentation format. + +.. option:: +yaml, +noyaml + + This option prints response data in YAML format. + +Files +~~~~~ + +|bind_keys| + +``/etc/resolv.conf`` + +See Also +~~~~~~~~ + +:iscman:`dig(1) `, :iscman:`named(8) `, :rfc:`4034`, :rfc:`4035`, :rfc:`4431`, :rfc:`5074`, :rfc:`5155`. diff --git a/bin/dig/Makefile.am b/bin/dig/Makefile.am new file mode 100644 index 0000000..32dea5b --- /dev/null +++ b/bin/dig/Makefile.am @@ -0,0 +1,39 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBIRS_CFLAGS) \ + $(LIBBIND9_CFLAGS) \ + $(LIBIDN2_CFLAGS) + +LDADD += \ + libdighost.la \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) \ + $(LIBBIND9_LIBS) \ + $(LIBIDN2_LIBS) + +noinst_LTLIBRARIES = libdighost.la + +libdighost_la_SOURCES = \ + dighost.h \ + dighost.c + +bin_PROGRAMS = dig host nslookup + +nslookup_CPPFLAGS = \ + $(AM_CPPFLAGS) + +nslookup_LDADD = \ + $(LDADD) + +if HAVE_READLINE +nslookup_CPPFLAGS += \ + $(READLINE_CFLAGS) +nslookup_LDADD += \ + $(READLINE_LIBS) +endif HAVE_READLINE diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in new file mode 100644 index 0000000..e5e09ba --- /dev/null +++ b/bin/dig/Makefile.in @@ -0,0 +1,897 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +bin_PROGRAMS = dig$(EXEEXT) host$(EXEEXT) nslookup$(EXEEXT) +@HAVE_READLINE_TRUE@am__append_2 = \ +@HAVE_READLINE_TRUE@ $(READLINE_CFLAGS) + +@HAVE_READLINE_TRUE@am__append_3 = \ +@HAVE_READLINE_TRUE@ $(READLINE_LIBS) + +subdir = bin/dig +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libdighost_la_LIBADD = +am_libdighost_la_OBJECTS = dighost.lo +libdighost_la_OBJECTS = $(am_libdighost_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +dig_SOURCES = dig.c +dig_OBJECTS = dig.$(OBJEXT) +dig_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +dig_DEPENDENCIES = libdighost.la $(LIBISC_LIBS) $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBIRS_LIBS) $(LIBBIND9_LIBS) \ + $(am__DEPENDENCIES_1) +host_SOURCES = host.c +host_OBJECTS = host.$(OBJEXT) +host_LDADD = $(LDADD) +host_DEPENDENCIES = libdighost.la $(LIBISC_LIBS) $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBIRS_LIBS) $(LIBBIND9_LIBS) \ + $(am__DEPENDENCIES_1) +nslookup_SOURCES = nslookup.c +nslookup_OBJECTS = nslookup-nslookup.$(OBJEXT) +am__DEPENDENCIES_2 = libdighost.la $(LIBISC_LIBS) $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBIRS_LIBS) $(LIBBIND9_LIBS) \ + $(am__DEPENDENCIES_1) +@HAVE_READLINE_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +nslookup_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/dig.Po ./$(DEPDIR)/dighost.Plo \ + ./$(DEPDIR)/host.Po ./$(DEPDIR)/nslookup-nslookup.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdighost_la_SOURCES) dig.c host.c nslookup.c +DIST_SOURCES = $(libdighost_la_SOURCES) dig.c host.c nslookup.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) $(LIBIRS_CFLAGS) $(LIBBIND9_CFLAGS) \ + $(LIBIDN2_CFLAGS) +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = libdighost.la $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) $(LIBBIND9_LIBS) $(LIBIDN2_LIBS) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +noinst_LTLIBRARIES = libdighost.la +libdighost_la_SOURCES = \ + dighost.h \ + dighost.c + +nslookup_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_2) +nslookup_LDADD = $(LDADD) $(am__append_3) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/dig/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/dig/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdighost.la: $(libdighost_la_OBJECTS) $(libdighost_la_DEPENDENCIES) $(EXTRA_libdighost_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libdighost_la_OBJECTS) $(libdighost_la_LIBADD) $(LIBS) + +dig$(EXEEXT): $(dig_OBJECTS) $(dig_DEPENDENCIES) $(EXTRA_dig_DEPENDENCIES) + @rm -f dig$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dig_OBJECTS) $(dig_LDADD) $(LIBS) + +host$(EXEEXT): $(host_OBJECTS) $(host_DEPENDENCIES) $(EXTRA_host_DEPENDENCIES) + @rm -f host$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(host_OBJECTS) $(host_LDADD) $(LIBS) + +nslookup$(EXEEXT): $(nslookup_OBJECTS) $(nslookup_DEPENDENCIES) $(EXTRA_nslookup_DEPENDENCIES) + @rm -f nslookup$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nslookup_OBJECTS) $(nslookup_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dig.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dighost.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/host.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nslookup-nslookup.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +nslookup-nslookup.o: nslookup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nslookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nslookup-nslookup.o -MD -MP -MF $(DEPDIR)/nslookup-nslookup.Tpo -c -o nslookup-nslookup.o `test -f 'nslookup.c' || echo '$(srcdir)/'`nslookup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nslookup-nslookup.Tpo $(DEPDIR)/nslookup-nslookup.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nslookup.c' object='nslookup-nslookup.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nslookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nslookup-nslookup.o `test -f 'nslookup.c' || echo '$(srcdir)/'`nslookup.c + +nslookup-nslookup.obj: nslookup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nslookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nslookup-nslookup.obj -MD -MP -MF $(DEPDIR)/nslookup-nslookup.Tpo -c -o nslookup-nslookup.obj `if test -f 'nslookup.c'; then $(CYGPATH_W) 'nslookup.c'; else $(CYGPATH_W) '$(srcdir)/nslookup.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nslookup-nslookup.Tpo $(DEPDIR)/nslookup-nslookup.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nslookup.c' object='nslookup-nslookup.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(nslookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nslookup-nslookup.obj `if test -f 'nslookup.c'; then $(CYGPATH_W) 'nslookup.c'; else $(CYGPATH_W) '$(srcdir)/nslookup.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/dig.Po + -rm -f ./$(DEPDIR)/dighost.Plo + -rm -f ./$(DEPDIR)/host.Po + -rm -f ./$(DEPDIR)/nslookup-nslookup.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/dig.Po + -rm -f ./$(DEPDIR)/dighost.Plo + -rm -f ./$(DEPDIR)/host.Po + -rm -f ./$(DEPDIR)/nslookup-nslookup.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-binPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am test-am test-local uninstall uninstall-am \ + uninstall-binPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/dig/dig.c b/bin/dig/dig.c new file mode 100644 index 0000000..87552be --- /dev/null +++ b/bin/dig/dig.c @@ -0,0 +1,3102 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dighost.h" + +#define ADD_STRING(b, s) \ + { \ + if (strlen(s) >= isc_buffer_availablelength(b)) { \ + return ((((ISC_R_NOSPACE)))); \ + } else { \ + isc_buffer_putstr(b, s); \ + } \ + } + +#define DIG_MAX_ADDRESSES 20 + +dig_lookup_t *default_lookup = NULL; + +static atomic_uintptr_t batchname = 0; +static FILE *batchfp = NULL; +static char *argv0; +static int addresscount = 0; + +static char domainopt[DNS_NAME_MAXTEXT]; +static char hexcookie[81]; + +static bool short_form = false, printcmd = true, plusquest = false, + pluscomm = false, ipv4only = false, ipv6only = false, digrc = true; +static uint32_t splitwidth = 0xffffffff; + +/*% opcode text */ +static const char *const opcodetext[] = { + "QUERY", "IQUERY", "STATUS", "RESERVED3", + "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7", + "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15" +}; + +static const char * +rcode_totext(dns_rcode_t rcode) { + static char buf[64]; + isc_buffer_t b; + isc_result_t result; + + memset(buf, 0, sizeof(buf)); + isc_buffer_init(&b, buf + 1, sizeof(buf) - 2); + result = dns_rcode_totext(rcode, &b); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (strspn(buf + 1, "0123456789") == strlen(buf + 1)) { + buf[0] = '?'; + return (buf); + } + return (buf + 1); +} + +/*% print usage */ +static void +print_usage(FILE *fp) { + fprintf(fp, + "Usage: dig [@global-server] [domain] [q-type] [q-class] " + "{q-opt}\n" + " {global-d-opt} host [@local-server] " + "{local-d-opt}\n" + " [ host [@local-server] {local-d-opt} [...]]\n"); +} + +#if TARGET_OS_IPHONE +static void +usage(void) { + fprintf(stderr, "Press for complete list of options\n"); +} +#else /* if TARGET_OS_IPHONE */ +noreturn static void +usage(void); + +static void +usage(void) { + print_usage(stderr); + fprintf(stderr, "\nUse \"dig -h\" (or \"dig -h | more\") " + "for complete list of options\n"); + exit(1); +} +#endif /* if TARGET_OS_IPHONE */ + +/*% version */ +static void +version(void) { + fprintf(stderr, "DiG %s\n", PACKAGE_VERSION); +} + +/*% help */ +static void +help(void) { + print_usage(stdout); + printf("Where: domain is in the Domain Name System\n" + " q-class is one of (in,hs,ch,...) [default: in]\n" + " q-type is one of " + "(a,any,mx,ns,soa,hinfo,axfr,txt,...) " + "[default:a]\n" + " (Use ixfr=version for type ixfr)\n" + " q-opt is one of:\n" + " -4 (use IPv4 query transport " + "only)\n" + " -6 (use IPv6 query transport " + "only)\n" + " -b address[#port] (bind to source " + "address/port)\n" + " -c class (specify query class)\n" + " -f filename (batch mode)\n" + " -k keyfile (specify tsig key file)\n" + " -m (enable memory usage " + "debugging)\n" + " -p port (specify port number)\n" + " -q name (specify query name)\n" + " -r (do not read ~/.digrc)\n" + " -t type (specify query type)\n" + " -u (display times in usec " + "instead of msec)\n" + " -x dot-notation (shortcut for reverse " + "lookups)\n" + " -y [hmac:]name:key (specify named base64 " + "tsig " + "key)\n" + " d-opt is of the form +keyword[=value], where " + "keyword " + "is:\n" + " +[no]aaflag (Set AA flag in query " + "(+[no]aaflag))\n" + " +[no]aaonly (Set AA flag in query " + "(+[no]aaflag))\n" + " +[no]additional (Control display of " + "additional section)\n" + " +[no]adflag (Set AD flag in query " + "(default on))\n" + " +[no]all (Set or clear all display " + "flags)\n" + " +[no]answer (Control display of " + "answer " + "section)\n" + " +[no]authority (Control display of " + "authority section)\n" + " +[no]badcookie (Retry BADCOOKIE " + "responses)\n" + " +[no]besteffort (Try to parse even " + "illegal " + "messages)\n" + " +bufsize[=###] (Set EDNS0 Max UDP packet " + "size)\n" + " +[no]cdflag (Set checking disabled " + "flag in query)\n" + " +[no]class (Control display of class " + "in records)\n" + " +[no]cmd (Control display of " + "command line -\n" + " global option)\n" + " +[no]comments (Control display of " + "packet " + "header\n" + " and section name " + "comments)\n" + " +[no]cookie (Add a COOKIE option to " + "the request)\n" + " +[no]crypto (Control display of " + "cryptographic\n" + " fields in records)\n" + " +[no]defname (Use search list " + "(+[no]search))\n" + " +[no]dns64prefix (Get the DNS64 prefixes " + "from ipv4only.arpa)\n" + " +[no]dnssec (Request DNSSEC records)\n" + " +domain=### (Set default domainname)\n" + " +[no]edns[=###] (Set EDNS version) [0]\n" + " +ednsflags=### (Set EDNS flag bits)\n" + " +[no]ednsnegotiation (Set EDNS version " + "negotiation)\n" + " +ednsopt=###[:value] (Send specified EDNS " + "option)\n" + " +noednsopt (Clear list of +ednsopt " + "options)\n" + " +[no]expandaaaa (Expand AAAA records)\n" + " +[no]expire (Request time to expire)\n" + " +[no]fail (Don't try next server on " + "SERVFAIL)\n" + " +[no]header-only (Send query without a " + "question section)\n" + " +[no]https[=###] (DNS-over-HTTPS mode) " + "[/]\n" + " +[no]https-get (Use GET instead of " + "default POST method while using HTTPS)\n" + " +[no]http-plain[=###] (DNS over plain HTTP " + "mode) " + "[/]\n" + " +[no]http-plain-get (Use GET instead of " + "default POST method while using plain HTTP)\n" + " +[no]identify (ID responders in short " + "answers)\n" +#ifdef HAVE_LIBIDN2 + " +[no]idnin (Parse IDN names " + "[default=on on tty])\n" + " +[no]idnout (Convert IDN response " + "[default=on on tty])\n" +#endif /* ifdef HAVE_LIBIDN2 */ + " +[no]ignore (Don't revert to TCP for " + "TC responses.)\n" + " +[no]keepalive (Request EDNS TCP " + "keepalive)\n" + " +[no]keepopen (Keep the TCP socket open " + "between " + "queries)\n" + " +[no]multiline (Print records in an " + "expanded format)\n" + " +ndots=### (Set search NDOTS value)\n" + " +[no]nsid (Request Name Server ID)\n" + " +[no]nssearch (Search all authoritative " + "nameservers)\n" + " +[no]onesoa (AXFR prints only one soa " + "record)\n" + " +[no]opcode=### (Set the opcode of the " + "request)\n" + " +padding=### (Set padding block size " + "[0])\n" + " +qid=### (Specify the query ID to " + "use when sending queries)\n" + " +[no]qr (Print question before " + "sending)\n" + " +[no]question (Control display of " + "question section)\n" + " +[no]raflag (Set RA flag in query " + "(+[no]raflag))\n" + " +[no]rdflag (Recursive mode " + "(+[no]recurse))\n" + " +[no]recurse (Recursive mode " + "(+[no]rdflag))\n" + " +retry=### (Set number of UDP " + "retries) [2]\n" + " +[no]rrcomments (Control display of " + "per-record " + "comments)\n" + " +[no]search (Set whether to use " + "searchlist)\n" + " +[no]short (Display nothing except " + "short\n" + " form of answers - global " + "option)\n" + " +[no]showbadcookie (Show BADCOOKIE message)\n" + " +[no]showsearch (Search with intermediate " + "results)\n" + " +[no]split=## (Split hex/base64 fields " + "into chunks)\n" + " +[no]stats (Control display of " + "statistics)\n" + " +subnet=addr (Set edns-client-subnet " + "option)\n" + " +[no]tcflag (Set TC flag in query " + "(+[no]tcflag))\n" + " +[no]tcp (TCP mode (+[no]vc))\n" + " +timeout=### (Set query timeout) [5]\n" + " +[no]tls (DNS-over-TLS mode)\n" + " +[no]tls-ca[=file] (Enable remote server's " + "TLS certificate validation)\n" + " +[no]tls-hostname=hostname (Explicitly set " + "the expected TLS hostname)\n" + " +[no]tls-certfile=file (Load client TLS " + "certificate chain from file)\n" + " +[no]tls-keyfile=file (Load client TLS " + "private key from file)\n" + " +[no]trace (Trace delegation down " + "from root " + "[+dnssec])\n" + " +tries=### (Set number of UDP " + "attempts) [3]\n" + " +[no]ttlid (Control display of ttls " + "in records)\n" + " +[no]ttlunits (Display TTLs in " + "human-readable units)\n" + " +[no]unknownformat (Print RDATA in RFC 3597 " + "\"unknown\" " + "format)\n" + " +[no]vc (TCP mode (+[no]tcp))\n" + " +[no]yaml (Present the results as " + "YAML)\n" + " +[no]zflag (Set Z flag in query)\n" + " global d-opts and servers (before host name) affect " + "all " + "queries.\n" + " local d-opts and servers (after host name) affect only " + "that lookup.\n" + " -h (print help and exit)\n" + " -v (print version " + "and exit)\n"); +} + +/*% + * Callback from dighost.c to print the received message. + */ +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + uint64_t diff; + time_t tnow; + struct tm tmnow; + char time_str[100]; + char fromtext[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(from, fromtext, sizeof(fromtext)); + + if (short_form || yaml) { + return; + } + + if (query->lookup->stats) { + const char *proto; + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + if (query->lookup->use_usec) { + printf(";; Query time: %ld usec\n", (long)diff); + } else { + printf(";; Query time: %ld msec\n", (long)diff / 1000); + } + if (dig_lookup_is_tls(query->lookup)) { + proto = "TLS"; + } else if (query->lookup->https_mode) { + if (query->lookup->http_plain) { + proto = query->lookup->https_get ? "HTTP-GET" + : "HTTP"; + } else { + proto = query->lookup->https_get ? "HTTPS-GET" + : "HTTPS"; + } + } else if (query->lookup->tcp_mode) { + proto = "TCP"; + } else { + proto = "UDP"; + } + printf(";; SERVER: %s(%s) (%s)\n", fromtext, query->userarg, + proto); + time(&tnow); + (void)localtime_r(&tnow, &tmnow); + + if (strftime(time_str, sizeof(time_str), + "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U) + { + printf(";; WHEN: %s\n", time_str); + } + if (query->lookup->doing_xfr) { + printf(";; XFR size: %u records (messages %u, " + "bytes %" PRIu64 ")\n", + query->rr_count, query->msg_count, + query->byte_count); + } else { + printf(";; MSG SIZE rcvd: %u\n", bytes); + } + if (tsigkey != NULL) { + if (!validated) { + puts(";; WARNING -- Some TSIG could not " + "be validated"); + } + } + if ((tsigkey == NULL) && (keysecret[0] != 0)) { + puts(";; WARNING -- TSIG key was not used."); + } + puts(""); + } else if (query->lookup->identify) { + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + if (query->lookup->use_usec) { + printf(";; Received %" PRIu64 " bytes " + "from %s(%s) in %ld us\n\n", + query->lookup->doing_xfr ? query->byte_count + : (uint64_t)bytes, + fromtext, query->userarg, (long)diff); + } else { + printf(";; Received %" PRIu64 " bytes " + "from %s(%s) in %ld ms\n\n", + query->lookup->doing_xfr ? query->byte_count + : (uint64_t)bytes, + fromtext, query->userarg, (long)diff / 1000); + } + } +} + +/* + * Callback from dighost.c to print that it is trying a server. + * Not used in dig. + * XXX print_trying + */ +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(frm); + UNUSED(lookup); +} + +/*% + * Internal print routine used to print short form replies. + */ +static isc_result_t +say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) { + isc_result_t result; + uint64_t diff; + char store[sizeof(" in 18446744073709551616 us.")]; + unsigned int styleflags = 0; + + if (query->lookup->trace || query->lookup->ns_search_only) { + result = dns_rdatatype_totext(rdata->type, buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + ADD_STRING(buf, " "); + } + + /* Turn on rrcomments if explicitly enabled */ + if (query->lookup->rrcomments > 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + if (query->lookup->nocrypto) { + styleflags |= DNS_STYLEFLAG_NOCRYPTO; + } + if (query->lookup->print_unknown_format) { + styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; + } + if (query->lookup->expandaaaa) { + styleflags |= DNS_STYLEFLAG_EXPANDAAAA; + } + result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0, splitwidth, + " ", buf); + if (result == ISC_R_NOSPACE) { + return (result); + } + check_result(result, "dns_rdata_totext"); + if (query->lookup->identify) { + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + ADD_STRING(buf, " from server "); + ADD_STRING(buf, query->servname); + if (query->lookup->use_usec) { + snprintf(store, sizeof(store), " in %" PRIu64 " us.", + diff); + } else { + snprintf(store, sizeof(store), " in %" PRIu64 " ms.", + diff / 1000); + } + ADD_STRING(buf, store); + } + ADD_STRING(buf, "\n"); + return (ISC_R_SUCCESS); +} + +/*% + * short_form message print handler. Calls above say_message() + */ +static isc_result_t +dns64prefix_answer(dns_message_t *msg, isc_buffer_t *buf) { + dns_rdataset_t *rdataset = NULL; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + isc_netprefix_t prefix[10]; + size_t i, count = 10; + + name = dns_fixedname_initname(&fixed); + result = dns_name_fromstring(name, "ipv4only.arpa", 0, NULL); + check_result(result, "dns_name_fromstring"); + + result = dns_message_findname(msg, DNS_SECTION_ANSWER, name, + dns_rdatatype_aaaa, dns_rdatatype_none, + NULL, &rdataset); + if (result == DNS_R_NXDOMAIN || result == DNS_R_NXRRSET) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + + result = dns_dns64_findprefix(rdataset, prefix, &count); + if (result == ISC_R_NOTFOUND) { + return (ISC_R_SUCCESS); + } + if (count > 10) { + count = 10; + } + for (i = 0; i < count; i++) { + result = isc_netaddr_totext(&prefix[i].addr, buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = isc_buffer_printf(buf, "/%u\n", prefix[i].prefixlen); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +/*% + * short_form message print handler. Calls above say_message() + */ +static isc_result_t +short_answer(dns_message_t *msg, dns_messagetextflag_t flags, isc_buffer_t *buf, + dig_query_t *query) { + dns_name_t *name; + dns_rdataset_t *rdataset; + isc_result_t result, loopresult; + dns_name_t empty_name; + dns_rdata_t rdata = DNS_RDATA_INIT; + + UNUSED(flags); + + dns_name_init(&empty_name, NULL); + result = dns_message_firstname(msg, DNS_SECTION_ANSWER); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + + for (;;) { + name = NULL; + dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + result = say_message(&rdata, query, buf); + if (result == ISC_R_NOSPACE) { + return (result); + } + check_result(result, "say_message"); + loopresult = dns_rdataset_next(rdataset); + dns_rdata_reset(&rdata); + } + } + result = dns_message_nextname(msg, DNS_SECTION_ANSWER); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +static bool +isdotlocal(dns_message_t *msg) { + isc_result_t result; + static unsigned char local_ndata[] = { "\005local" }; + static unsigned char local_offsets[] = { 0, 6 }; + static dns_name_t local = DNS_NAME_INITABSOLUTE(local_ndata, + local_offsets); + + for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); + result == ISC_R_SUCCESS; + result = dns_message_nextname(msg, DNS_SECTION_QUESTION)) + { + dns_name_t *name = NULL; + dns_message_currentname(msg, DNS_SECTION_QUESTION, &name); + if (dns_name_issubdomain(name, &local)) { + return (true); + } + } + return (false); +} + +/* + * Callback from dighost.c to print the reply from a server + */ +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + isc_result_t result; + dns_messagetextflag_t flags; + isc_buffer_t *buf = NULL; + unsigned int len = OUTPUTBUF; + dns_master_style_t *style = NULL; + unsigned int styleflags = 0; + bool isquery = (msg == query->lookup->sendmsg); + bool dns64prefix = query->lookup->dns64prefix; + + UNUSED(msgbuf); + + dig_idnsetup(query->lookup, true); + + styleflags |= DNS_STYLEFLAG_REL_OWNER; + if (yaml) { + msg->indent.string = " "; + msg->indent.count = 3; + styleflags |= DNS_STYLEFLAG_YAML; + } else { + if (query->lookup->comments) { + styleflags |= DNS_STYLEFLAG_COMMENT; + } + if (query->lookup->print_unknown_format) { + styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; + } + /* Turn on rrcomments if explicitly enabled */ + if (query->lookup->rrcomments > 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + if (query->lookup->ttlunits) { + styleflags |= DNS_STYLEFLAG_TTL_UNITS; + } + if (query->lookup->nottl) { + styleflags |= DNS_STYLEFLAG_NO_TTL; + } + if (query->lookup->noclass) { + styleflags |= DNS_STYLEFLAG_NO_CLASS; + } + if (query->lookup->nocrypto) { + styleflags |= DNS_STYLEFLAG_NOCRYPTO; + } + if (query->lookup->expandaaaa) { + styleflags |= DNS_STYLEFLAG_EXPANDAAAA; + } + if (query->lookup->multiline) { + styleflags |= DNS_STYLEFLAG_OMIT_OWNER; + styleflags |= DNS_STYLEFLAG_OMIT_CLASS; + styleflags |= DNS_STYLEFLAG_REL_DATA; + styleflags |= DNS_STYLEFLAG_OMIT_TTL; + styleflags |= DNS_STYLEFLAG_TTL; + styleflags |= DNS_STYLEFLAG_MULTILINE; + /* Turn on rrcomments unless explicitly disabled */ + if (query->lookup->rrcomments >= 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + } + } + if (query->lookup->multiline || + (query->lookup->nottl && query->lookup->noclass)) + { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, + 32, 80, 8, splitwidth, mctx); + } else if (query->lookup->nottl || query->lookup->noclass) { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, + 40, 80, 8, splitwidth, mctx); + } else { + result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, + 48, 80, 8, splitwidth, mctx); + } + check_result(result, "dns_master_stylecreate"); + + if (query->lookup->cmdline[0] != 0) { + if (!short_form && !dns64prefix && printcmd) { + printf("%s", query->lookup->cmdline); + } + query->lookup->cmdline[0] = '\0'; + } + debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders", + query->lookup->comments ? "comments" : "nocomments", + short_form ? "short_form" + : dns64prefix ? "dns64prefix_form" + : "long_form"); + + flags = 0; + if (!headers) { + flags |= DNS_MESSAGETEXTFLAG_NOHEADERS; + flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; + } + if (query->lookup->onesoa && + query->lookup->rdtype == dns_rdatatype_axfr) + { + flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA + : DNS_MESSAGETEXTFLAG_OMITSOA; + } + if (!query->lookup->comments) { + flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; + } + + isc_buffer_allocate(mctx, &buf, len); + + if (yaml) { + enum { Q = 0x1, R = 0x2 }; /* Q:query; R:ecursive */ + unsigned int tflag = 0; + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + uint16_t sport; + char *hash; + int pf; + + printf("-\n"); + printf(" type: MESSAGE\n"); + printf(" message:\n"); + + if (isquery) { + tflag |= Q; + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + tflag |= R; + } + } else if (((msg->flags & DNS_MESSAGEFLAG_RD) != 0) && + ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)) + { + tflag |= R; + } + + if (tflag == (Q | R)) { + printf(" type: RECURSIVE_QUERY\n"); + } else if (tflag == Q) { + printf(" type: AUTH_QUERY\n"); + } else if (tflag == R) { + printf(" type: RECURSIVE_RESPONSE\n"); + } else { + printf(" type: AUTH_RESPONSE\n"); + } + + if (!isc_time_isepoch(&query->time_sent)) { + char tbuf[100]; + if (query->lookup->use_usec) { + isc_time_formatISO8601us(&query->time_sent, + tbuf, sizeof(tbuf)); + } else { + isc_time_formatISO8601ms(&query->time_sent, + tbuf, sizeof(tbuf)); + } + printf(" query_time: !!timestamp %s\n", tbuf); + } + + if (!isquery && !isc_time_isepoch(&query->time_recv)) { + char tbuf[100]; + if (query->lookup->use_usec) { + isc_time_formatISO8601us(&query->time_recv, + tbuf, sizeof(tbuf)); + } else { + isc_time_formatISO8601ms(&query->time_recv, + tbuf, sizeof(tbuf)); + } + printf(" response_time: !!timestamp %s\n", tbuf); + } + + printf(" message_size: %ub\n", + isc_buffer_usedlength(msgbuf)); + + pf = isc_sockaddr_pf(&query->sockaddr); + if (pf == PF_INET || pf == PF_INET6) { + printf(" socket_family: %s\n", + pf == PF_INET ? "INET" : "INET6"); + + printf(" socket_protocol: %s\n", + query->lookup->tcp_mode ? "TCP" : "UDP"); + + sport = isc_sockaddr_getport(&query->sockaddr); + isc_sockaddr_format(&query->sockaddr, sockstr, + sizeof(sockstr)); + hash = strchr(sockstr, '#'); + if (hash != NULL) { + *hash = '\0'; + } + if (strcmp(sockstr, "::") == 0) { + strlcat(sockstr, "0", sizeof(sockstr)); + } + + printf(" response_address: \"%s\"\n", sockstr); + printf(" response_port: %u\n", sport); + } + + if (query->handle != NULL) { + isc_sockaddr_t saddr = + isc_nmhandle_localaddr(query->handle); + sport = isc_sockaddr_getport(&saddr); + isc_sockaddr_format(&saddr, sockstr, sizeof(sockstr)); + hash = strchr(sockstr, '#'); + if (hash != NULL) { + *hash = '\0'; + } + if (strcmp(sockstr, "::") == 0) { + strlcat(sockstr, "0", sizeof(sockstr)); + } + + printf(" query_address: \"%s\"\n", sockstr); + printf(" query_port: %u\n", sport); + } + + printf(" %s:\n", isquery ? "query_message_data" + : "response_message_data"); + result = dns_message_headertotext(msg, style, flags, buf); + } else if (query->lookup->comments && !short_form && !dns64prefix) { + if (query->lookup->cmdline[0] != '\0' && printcmd) { + printf("; %s\n", query->lookup->cmdline); + } + if (msg == query->lookup->sendmsg) { + printf(";; Sending:\n"); + } else { + printf(";; Got answer:\n"); + } + + if (headers) { + if (isdotlocal(msg)) { + printf(";; WARNING: .local is reserved for " + "Multicast DNS\n;; You are currently " + "testing what happens when an mDNS " + "query is leaked to DNS\n"); + } + printf(";; ->>HEADER<<- opcode: %s, status: %s, " + "id: %u\n", + opcodetext[msg->opcode], + rcode_totext(msg->rcode), msg->id); + printf(";; flags:"); + if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) { + printf(" qr"); + } + if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) { + printf(" aa"); + } + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { + printf(" tc"); + } + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + printf(" rd"); + } + if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) { + printf(" ra"); + } + if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) { + printf(" ad"); + } + if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) { + printf(" cd"); + } + if ((msg->flags & 0x0040U) != 0) { + printf("; MBZ: 0x4"); + } + + printf("; QUERY: %u, ANSWER: %u, " + "AUTHORITY: %u, ADDITIONAL: %u\n", + msg->counts[DNS_SECTION_QUESTION], + msg->counts[DNS_SECTION_ANSWER], + msg->counts[DNS_SECTION_AUTHORITY], + msg->counts[DNS_SECTION_ADDITIONAL]); + + if (msg != query->lookup->sendmsg && + (msg->flags & DNS_MESSAGEFLAG_RD) != 0 && + (msg->flags & DNS_MESSAGEFLAG_RA) == 0) + { + printf(";; WARNING: recursion requested " + "but not available\n"); + } + } + if (msg != query->lookup->sendmsg && + query->lookup->edns != -1 && msg->opt == NULL && + (msg->rcode == dns_rcode_formerr || + msg->rcode == dns_rcode_notimp)) + { + printf("\n;; WARNING: EDNS query returned status " + "%s - retry with '%s+noedns'\n", + rcode_totext(msg->rcode), + query->lookup->dnssec ? "+nodnssec " : ""); + } + if (msg != query->lookup->sendmsg && extrabytes != 0U) { + printf(";; WARNING: Message has %u extra byte%s at " + "end\n", + extrabytes, extrabytes != 0 ? "s" : ""); + } + } + +repopulate_buffer: + + if (query->lookup->comments && headers && !short_form && !dns64prefix) { + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_OPT, style, flags, buf); + if (result == ISC_R_NOSPACE) { + buftoosmall: + len += OUTPUTBUF; + isc_buffer_free(&buf); + isc_buffer_allocate(mctx, &buf, len); + goto repopulate_buffer; + } + check_result(result, "dns_message_pseudosectiontotext"); + } + + if (query->lookup->section_question && headers) { + if (!short_form && !dns64prefix) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_QUESTION, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } + } + if (query->lookup->section_answer) { + if (!short_form && !dns64prefix) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_ANSWER, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } else if (dns64prefix) { + result = dns64prefix_answer(msg, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns64prefix_answer"); + } else { + result = short_answer(msg, flags, buf, query); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "short_answer"); + } + } + if (query->lookup->section_authority) { + if (!short_form && !dns64prefix) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_AUTHORITY, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } + } + if (query->lookup->section_additional) { + if (!short_form && !dns64prefix) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_ADDITIONAL, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + /* + * Only print the signature on the first record. + */ + if (headers) { + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_TSIG, style, + flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_" + "pseudosectiontotext"); + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_SIG0, style, + flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_" + "pseudosectiontotext"); + } + } + } + + if (headers && query->lookup->comments && !short_form && !yaml) { + printf("\n"); + } + + printf("%.*s", (int)isc_buffer_usedlength(buf), + (char *)isc_buffer_base(buf)); + isc_buffer_free(&buf); + + if (style != NULL) { + dns_master_styledestroy(&style, mctx); + } + + dig_idnsetup(query->lookup, false); + + return (result); +} + +/*% + * print the greeting message when the program first starts up. + */ +static void +printgreeting(int argc, char **argv, dig_lookup_t *lookup) { + int i; + static bool first = true; + char append[MXNAME]; + + if (printcmd) { + snprintf(lookup->cmdline, sizeof(lookup->cmdline), + "%s; <<>> DiG %s <<>>", first ? "\n" : "", + PACKAGE_VERSION); + i = 1; + while (i < argc) { + snprintf(append, sizeof(append), " %s", argv[i++]); + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline)); + if (first && addresscount != 0) { + snprintf(append, sizeof(append), + "; (%d server%s found)\n", addresscount, + addresscount > 1 ? "s" : ""); + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + if (first) { + snprintf(append, sizeof(append), + ";; global options:%s%s\n", + short_form ? " +short" : "", + printcmd ? " +cmd" : ""); + first = false; + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + } +} + +#define FULLCHECK(A) \ + do { \ + size_t _l = strlen(cmd); \ + if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ + goto invalid_option; \ + } while (0) +#define FULLCHECK2(A, B) \ + do { \ + size_t _l = strlen(cmd); \ + if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ + (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \ + goto invalid_option; \ + } while (0) +#define FULLCHECK6(A, B, C, D, E, F) \ + do { \ + size_t _l = strlen(cmd); \ + if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ + (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0) && \ + (_l >= sizeof(C) || strncasecmp(cmd, C, _l) != 0) && \ + (_l >= sizeof(D) || strncasecmp(cmd, D, _l) != 0) && \ + (_l >= sizeof(E) || strncasecmp(cmd, E, _l) != 0) && \ + (_l >= sizeof(F) || strncasecmp(cmd, F, _l) != 0)) \ + goto invalid_option; \ + } while (0) + +static bool +plus_tls_options(const char *cmd, const char *value, const bool state, + dig_lookup_t *lookup) { + /* + * Using TLS implies "TCP-like" mode. + */ + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = state; + } + switch (cmd[3]) { + case '-': + /* + * Assume that if any of the +tls-* options are set, then we + * need to verify the remote certificate (compatibility with + * kdig). + */ + if (state) { + lookup->tls_ca_set = state; + } + switch (cmd[4]) { + case 'c': + switch (cmd[5]) { + case 'a': + FULLCHECK("tls-ca"); + lookup->tls_ca_set = state; + if (state && value != NULL) { + lookup->tls_ca_file = + isc_mem_strdup(mctx, value); + } + break; + case 'e': + FULLCHECK("tls-certfile"); + lookup->tls_cert_file_set = state; + if (state) { + if (value != NULL && *value != '\0') { + lookup->tls_cert_file = + isc_mem_strdup(mctx, + value); + } else { + fprintf(stderr, + ";; TLS certificate " + "file is " + "not specified\n"); + goto invalid_option; + } + } + break; + default: + goto invalid_option; + } + break; + case 'h': + FULLCHECK("tls-hostname"); + lookup->tls_hostname_set = state; + if (state) { + if (value != NULL && *value != '\0') { + lookup->tls_hostname = + isc_mem_strdup(mctx, value); + } else { + fprintf(stderr, ";; TLS hostname is " + "not specified\n"); + goto invalid_option; + } + } + break; + case 'k': + FULLCHECK("tls-keyfile"); + lookup->tls_key_file_set = state; + if (state) { + if (value != NULL && *value != '\0') { + lookup->tls_key_file = + isc_mem_strdup(mctx, value); + } else { + fprintf(stderr, + ";; TLS private key file is " + "not specified\n"); + goto invalid_option; + } + } + break; + default: + goto invalid_option; + } + break; + case '\0': + FULLCHECK("tls"); + lookup->tls_mode = state; + break; + default: + goto invalid_option; + } + + return true; +invalid_option: + return false; +} + +/*% + * We're not using isc_commandline_parse() here since the command line + * syntax of dig is quite a bit different from that which can be described + * by that routine. + * XXX doc options + */ + +static dig_lookup_t * +plus_option(char *option, bool is_batchfile, bool *need_clone, + dig_lookup_t *lookup) { + isc_result_t result; + char *cmd, *value, *last = NULL, *code, *extra; + uint32_t num; + bool state = true; + size_t n; + + INSIST(option != NULL); + + if ((cmd = strtok_r(option, "=", &last)) == NULL) { + printf(";; Invalid option %s\n", option); + return (lookup); + } + if (strncasecmp(cmd, "no", 2) == 0) { + cmd += 2; + state = false; + } + /* parse the rest of the string */ + value = strtok_r(NULL, "", &last); + + switch (cmd[0]) { + case 'a': + switch (cmd[1]) { + case 'a': /* aaonly / aaflag */ + FULLCHECK2("aaonly", "aaflag"); + lookup->aaonly = state; + break; + case 'd': + switch (cmd[2]) { + case 'd': /* additional */ + FULLCHECK("additional"); + lookup->section_additional = state; + break; + case 'f': /* adflag */ + case '\0': /* +ad is a synonym for +adflag */ + FULLCHECK("adflag"); + lookup->adflag = state; + break; + default: + goto invalid_option; + } + break; + case 'l': /* all */ + FULLCHECK("all"); + lookup->section_question = state; + lookup->section_authority = state; + lookup->section_answer = state; + lookup->section_additional = state; + lookup->comments = state; + lookup->stats = state; + printcmd = state; + break; + case 'n': /* answer */ + FULLCHECK("answer"); + lookup->section_answer = state; + break; + case 'u': /* authority */ + FULLCHECK("authority"); + lookup->section_authority = state; + break; + default: + goto invalid_option; + } + break; + case 'b': + switch (cmd[1]) { + case 'a': /* badcookie */ + FULLCHECK("badcookie"); + lookup->badcookie = state; + break; + case 'e': /* besteffort */ + FULLCHECK("besteffort"); + lookup->besteffort = state; + break; + case 'u': /* bufsize */ + FULLCHECK("bufsize"); + if (!state) { + goto invalid_option; + } + if (value == NULL) { + lookup->udpsize = DEFAULT_EDNS_BUFSIZE; + break; + } + result = parse_uint(&num, value, COMMSIZE, + "buffer size"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse buffer size"); + goto exit_or_usage; + } + lookup->udpsize = num; + break; + default: + goto invalid_option; + } + break; + case 'c': + switch (cmd[1]) { + case 'd': /* cdflag */ + switch (cmd[2]) { + case 'f': /* cdflag */ + case '\0': /* +cd is a synonym for +cdflag */ + FULLCHECK("cdflag"); + lookup->cdflag = state; + break; + default: + goto invalid_option; + } + break; + case 'l': /* class */ + /* keep +cl for backwards compatibility */ + FULLCHECK2("cl", "class"); + lookup->noclass = !state; + break; + case 'm': /* cmd */ + FULLCHECK("cmd"); + printcmd = state; + break; + case 'o': /* comments */ + switch (cmd[2]) { + case 'm': + FULLCHECK("comments"); + lookup->comments = state; + if (lookup == default_lookup) { + pluscomm = state; + } + break; + case 'o': /* cookie */ + FULLCHECK("cookie"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + lookup->sendcookie = state; + if (value != NULL) { + n = strlcpy(hexcookie, value, + sizeof(hexcookie)); + if (n >= sizeof(hexcookie)) { + warn("COOKIE data too large"); + goto exit_or_usage; + } + lookup->cookie = hexcookie; + } else { + lookup->cookie = NULL; + } + break; + default: + goto invalid_option; + } + break; + case 'r': + FULLCHECK("crypto"); + lookup->nocrypto = !state; + break; + default: + goto invalid_option; + } + break; + case 'd': + switch (cmd[1]) { + case 'e': /* defname */ + FULLCHECK("defname"); + if (!lookup->trace) { + usesearch = state; + } + break; + case 'n': + switch (cmd[2]) { + case 's': + switch (cmd[3]) { + case '6': /* dns64prefix */ + FULLCHECK("dns64prefix"); + if (state) { + if (*need_clone) { + lookup = clone_lookup( + default_lookup, + true); + } + *need_clone = true; + lookup->dns64prefix = state; + strlcpy(lookup->textname, + "ipv4only.arpa", + sizeof(lookup->textname)); + printcmd = false; + lookup->section_additional = + false; + lookup->section_answer = true; + lookup->section_authority = + false; + lookup->section_question = + false; + lookup->comments = false; + lookup->stats = false; + lookup->rrcomments = -1; + lookup->rdtype = + dns_rdatatype_aaaa; + lookup->rdtypeset = true; + ISC_LIST_APPEND(lookup_list, + lookup, link); + } + break; + case 's': /* dnssec */ + FULLCHECK("dnssec"); + dnssec: + if (state && lookup->edns == -1) { + lookup->edns = + DEFAULT_EDNS_VERSION; + } + lookup->dnssec = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'o': /* domain ... but treat "do" as synonym for dnssec */ + if (cmd[2] == '\0') { + goto dnssec; + } + FULLCHECK("domain"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + strlcpy(domainopt, value, sizeof(domainopt)); + break; + case 's': /* dscp */ + /* obsolete */ + FULLCHECK("dscp"); + fprintf(stderr, ";; +dscp option is obsolete " + "and has no effect"); + break; + default: + goto invalid_option; + } + break; + case 'e': + switch (cmd[1]) { + case 'd': + switch (cmd[2]) { + case 'n': + switch (cmd[3]) { + case 's': + switch (cmd[4]) { + case 0: + FULLCHECK("edns"); + if (!state) { + lookup->edns = -1; + break; + } + if (value == NULL) { + lookup->edns = + DEFAULT_EDNS_VERSION; + break; + } + result = parse_uint(&num, value, + 255, + "edns"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse " + "edns"); + goto exit_or_usage; + } + lookup->edns = num; + break; + case 'f': + FULLCHECK("ednsflags"); + if (!state) { + lookup->ednsflags = 0; + break; + } + if (value == NULL) { + lookup->ednsflags = 0; + break; + } + result = parse_xint( + &num, value, 0xffff, + "ednsflags"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse " + "ednsflags"); + goto exit_or_usage; + } + lookup->ednsflags = num; + break; + case 'n': + FULLCHECK("ednsnegotiation"); + lookup->ednsneg = state; + break; + case 'o': + FULLCHECK("ednsopt"); + if (!state) { + lookup->ednsoptscnt = 0; + break; + } + code = NULL; + if (value != NULL) { + code = strtok_r(value, + ":", + &last); + } + if (code == NULL) { + warn("ednsopt no " + "code point " + "specified"); + goto exit_or_usage; + } + extra = strtok_r(NULL, "\0", + &last); + save_opt(lookup, code, extra); + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'x': + switch (cmd[2]) { + case 'p': + switch (cmd[3]) { + case 'a': + FULLCHECK("expandaaaa"); + lookup->expandaaaa = state; + break; + case 'i': + FULLCHECK("expire"); + lookup->expire = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'f': /* fail */ + switch (cmd[1]) { + case 'a': + FULLCHECK("fail"); + lookup->servfail_stops = state; + break; + case 'u': + FULLCHECK("fuzztime"); + lookup->fuzzing = state; + if (lookup->fuzzing) { + if (value == NULL) { + lookup->fuzztime = 0x622acce1; + break; + } + result = parse_uint(&num, value, 0xffffffff, + "fuzztime"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse fuzztime"); + goto exit_or_usage; + } + lookup->fuzztime = num; + } + break; + default: + goto invalid_option; + } + break; + case 'h': + switch (cmd[1]) { + case 'e': /* header-only */ + FULLCHECK("header-only"); + lookup->header_only = state; + break; + case 't': + FULLCHECK6("https", "https-get", "https-post", + "http-plain", "http-plain-get", + "http-plain-post"); +#if HAVE_LIBNGHTTP2 + if (lookup->https_path != NULL) { + isc_mem_free(mctx, lookup->https_path); + lookup->https_path = NULL; + } + if (!state) { + lookup->https_mode = false; + break; + } + lookup->https_mode = true; + if (cmd[4] == '-') { + lookup->http_plain = true; + switch (cmd[10]) { + case '\0': + FULLCHECK("http-plain"); + break; + case '-': + switch (cmd[11]) { + case 'p': + FULLCHECK("http-plain-post"); + break; + case 'g': + FULLCHECK("http-plain-get"); + lookup->https_get = true; + break; + } + break; + default: + goto invalid_option; + } + } else { + switch (cmd[5]) { + case '\0': + FULLCHECK("https"); + break; + case '-': + switch (cmd[6]) { + case 'p': + FULLCHECK("https-post"); + break; + case 'g': + FULLCHECK("https-get"); + lookup->https_get = true; + break; + } + break; + default: + goto invalid_option; + } + } + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = state; + } + if (value == NULL) { + lookup->https_path = isc_mem_strdup( + mctx, ISC_NM_HTTP_DEFAULT_PATH); + } else { + if (!isc_nm_http_path_isvalid(value)) { + fprintf(stderr, + ";; The given HTTP path \"%s\" " + "is not " + "a valid absolute path\n", + value); + goto invalid_option; + } + lookup->https_path = isc_mem_strdup(mctx, + value); + } +#else + fprintf(stderr, ";; DoH support not enabled\n"); +#endif + break; + default: + goto invalid_option; + } + break; + case 'i': + switch (cmd[1]) { + case 'd': /* identify */ + switch (cmd[2]) { + case 'e': + FULLCHECK("identify"); + lookup->identify = state; + break; + case 'n': + switch (cmd[3]) { + case 'i': + FULLCHECK("idnin"); +#ifndef HAVE_LIBIDN2 + fprintf(stderr, ";; IDN input support" + " not enabled\n"); +#else /* ifndef HAVE_LIBIDN2 */ + lookup->idnin = state; +#endif /* ifndef HAVE_LIBIDN2 */ + break; + case 'o': + FULLCHECK("idnout"); +#ifndef HAVE_LIBIDN2 + fprintf(stderr, ";; IDN output support" + " not enabled\n"); +#else /* ifndef HAVE_LIBIDN2 */ + lookup->idnout = state; +#endif /* ifndef HAVE_LIBIDN2 */ + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'g': /* ignore */ + default: /* + * Inherits default for compatibility (+[no]i*). + */ + FULLCHECK("ignore"); + lookup->ignore = state; + } + break; + case 'k': + switch (cmd[1]) { + case 'e': + switch (cmd[2]) { + case 'e': + switch (cmd[3]) { + case 'p': + switch (cmd[4]) { + case 'a': + FULLCHECK("keepalive"); + lookup->tcp_keepalive = state; + break; + case 'o': + FULLCHECK("keepopen"); + keep_open = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'm': /* multiline */ + switch (cmd[1]) { + case 'a': + FULLCHECK("mapped"); + fprintf(stderr, ";; +mapped option is deprecated"); + break; + case 'u': + FULLCHECK("multiline"); + lookup->multiline = state; + break; + default: + goto invalid_option; + } + break; + case 'n': + switch (cmd[1]) { + case 'd': /* ndots */ + FULLCHECK("ndots"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&num, value, MAXNDOTS, "ndots"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse ndots"); + goto exit_or_usage; + } + ndots = num; + break; + case 's': + switch (cmd[2]) { + case 'i': /* nsid */ + FULLCHECK("nsid"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + lookup->nsid = state; + break; + case 's': /* nssearch */ + FULLCHECK("nssearch"); + lookup->ns_search_only = state; + if (state) { + lookup->trace_root = true; + lookup->recurse = true; + lookup->identify = true; + lookup->stats = false; + lookup->comments = false; + lookup->section_additional = false; + lookup->section_authority = false; + lookup->section_question = false; + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + short_form = true; + lookup->rrcomments = 0; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'o': + switch (cmd[1]) { + case 'n': + FULLCHECK("onesoa"); + lookup->onesoa = state; + break; + case 'p': + FULLCHECK("opcode"); + if (!state) { + lookup->opcode = 0; /* default - query */ + break; + } + if (value == NULL) { + goto need_value; + } + for (num = 0; + num < sizeof(opcodetext) / sizeof(opcodetext[0]); + num++) + { + if (strcasecmp(opcodetext[num], value) == 0) { + break; + } + } + if (num < 16) { + lookup->opcode = (dns_opcode_t)num; + break; + } + result = parse_uint(&num, value, 15, "opcode"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse opcode"); + goto exit_or_usage; + } + lookup->opcode = (dns_opcode_t)num; + break; + default: + goto invalid_option; + } + break; + case 'p': + FULLCHECK("padding"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, 512, "padding"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse padding"); + goto exit_or_usage; + } + lookup->padding = (uint16_t)num; + break; + case 'q': + switch (cmd[1]) { + case 'i': /* qid */ + FULLCHECK("qid"); + if (!state) { + lookup->setqid = false; + lookup->qid = 0; + break; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, MAXQID, "qid"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse qid"); + goto exit_or_usage; + } + lookup->setqid = true; + lookup->qid = num; + break; + case 'r': /* qr */ + FULLCHECK("qr"); + lookup->qr = state; + break; + case 'u': /* question */ + FULLCHECK("question"); + lookup->section_question = state; + if (lookup == default_lookup) { + plusquest = state; + } + break; + default: + goto invalid_option; + } + break; + case 'r': + switch (cmd[1]) { + case 'a': /* raflag */ + FULLCHECK("raflag"); + lookup->raflag = state; + break; + case 'd': /* rdflag */ + FULLCHECK("rdflag"); + lookup->recurse = state; + break; + case 'e': + switch (cmd[2]) { + case 'c': /* recurse */ + FULLCHECK("recurse"); + lookup->recurse = state; + break; + case 't': /* retry / retries */ + FULLCHECK2("retry", "retries"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&lookup->retries, value, + MAXTRIES - 1, "retries"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse retries"); + goto exit_or_usage; + } + lookup->retries++; + break; + default: + goto invalid_option; + } + break; + case 'r': /* rrcomments */ + FULLCHECK("rrcomments"); + lookup->rrcomments = state ? 1 : -1; + break; + default: + goto invalid_option; + } + break; + case 's': + switch (cmd[1]) { + case 'e': /* search */ + FULLCHECK("search"); + if (!lookup->trace) { + usesearch = state; + } + break; + case 'h': + if (cmd[2] != 'o') { + goto invalid_option; + } + switch (cmd[3]) { + case 'r': /* short */ + FULLCHECK("short"); + short_form = state; + if (state) { + printcmd = false; + lookup->section_additional = false; + lookup->section_answer = true; + lookup->section_authority = false; + lookup->section_question = false; + lookup->comments = false; + lookup->stats = false; + lookup->rrcomments = -1; + } + break; + case 'w': /* showsearch */ + switch (cmd[4]) { + case 'b': + FULLCHECK("showbadcookie"); + lookup->showbadcookie = state; + break; + case 's': + FULLCHECK("showsearch"); + if (!lookup->trace) { + showsearch = state; + usesearch = state; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'i': /* sigchase */ + FULLCHECK("sigchase"); + fprintf(stderr, ";; +sigchase option is deprecated"); + break; + case 'p': /* split */ + FULLCHECK("split"); + if (value != NULL && !state) { + goto invalid_option; + } + if (!state) { + splitwidth = 0; + break; + } else if (value == NULL) { + break; + } + + result = parse_uint(&splitwidth, value, 1023, "split"); + if ((splitwidth % 4) != 0U) { + splitwidth = ((splitwidth + 3) / 4) * 4; + fprintf(stderr, + ";; Warning, split must be " + "a multiple of 4; adjusting " + "to %u\n", + splitwidth); + } + /* + * There is an adjustment done in the + * totext_() functions which causes + * splitwidth to shrink. This is okay when we're + * using the default width but incorrect in this + * case, so we correct for it + */ + if (splitwidth) { + splitwidth += 3; + } + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse split"); + goto exit_or_usage; + } + break; + case 't': /* stats */ + FULLCHECK("stats"); + lookup->stats = state; + break; + case 'u': /* subnet */ + FULLCHECK("subnet"); + if (state && value == NULL) { + goto need_value; + } + if (!state) { + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + lookup->ecs_addr = NULL; + } + break; + } + if (lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + lookup->ecs_addr = NULL; + } + result = parse_netprefix(&lookup->ecs_addr, value); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse client"); + goto exit_or_usage; + } + break; + default: + goto invalid_option; + } + break; + case 't': + switch (cmd[1]) { + case 'c': /* tcp */ + switch (cmd[2]) { + case 'f': + FULLCHECK("tcflag"); + lookup->tcflag = state; + break; + case 'p': + FULLCHECK("tcp"); + if (!is_batchfile) { + lookup->tcp_mode = state; + lookup->tcp_mode_set = true; + } + break; + default: + goto invalid_option; + } + break; + case 'i': /* timeout */ + FULLCHECK("timeout"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&timeout, value, MAXTIMEOUT, + "timeout"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse timeout"); + goto exit_or_usage; + } + if (timeout == 0) { + timeout = 1; + } + break; + case 'l': + switch (cmd[2]) { + case 's': + if (!plus_tls_options(cmd, value, state, + lookup)) + { + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'o': + FULLCHECK("topdown"); + fprintf(stderr, ";; +topdown option is deprecated"); + break; + case 'r': + switch (cmd[2]) { + case 'a': /* trace */ + FULLCHECK("trace"); + lookup->trace = state; + lookup->trace_root = state; + if (state) { + lookup->recurse = true; + lookup->identify = true; + lookup->comments = false; + lookup->rrcomments = 0; + lookup->stats = false; + lookup->section_additional = false; + lookup->section_authority = true; + lookup->section_question = false; + lookup->dnssec = true; + lookup->sendcookie = true; + usesearch = false; + } + break; + case 'i': /* tries */ + FULLCHECK("tries"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&lookup->retries, value, + MAXTRIES, "tries"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse tries"); + goto exit_or_usage; + } + if (lookup->retries == 0) { + lookup->retries = 1; + } + break; + case 'u': /* trusted-key */ + FULLCHECK("trusted-key"); + fprintf(stderr, ";; +trusted-key option is " + "deprecated"); + break; + default: + goto invalid_option; + } + break; + case 't': + switch (cmd[2]) { + case 'l': + switch (cmd[3]) { + case 0: + case 'i': /* ttlid */ + FULLCHECK2("ttl", "ttlid"); + lookup->nottl = !state; + break; + case 'u': /* ttlunits */ + FULLCHECK("ttlunits"); + lookup->nottl = false; + lookup->ttlunits = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'u': + switch (cmd[1]) { + case 'n': + switch (cmd[2]) { + case 'e': + FULLCHECK("unexpected"); + fprintf(stderr, ";; +unexpected option " + "is deprecated"); + break; + case 'k': + FULLCHECK("unknownformat"); + lookup->print_unknown_format = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + + break; + case 'v': + FULLCHECK("vc"); + if (!is_batchfile) { + lookup->tcp_mode = state; + lookup->tcp_mode_set = true; + } + break; + case 'y': /* yaml */ + FULLCHECK("yaml"); + yaml = state; + if (state) { + printcmd = false; + lookup->stats = false; + lookup->rrcomments = -1; + } + break; + case 'z': /* zflag */ + FULLCHECK("zflag"); + lookup->zflag = state; + break; + default: + invalid_option: + need_value: +#if TARGET_OS_IPHONE + exit_or_usage: +#endif /* if TARGET_OS_IPHONE */ + fprintf(stderr, "Invalid option: +%s\n", option); + usage(); + } + return (lookup); + +#if !TARGET_OS_IPHONE +exit_or_usage: + cleanup_openssl_refs(); + digexit(); +#endif /* if !TARGET_OS_IPHONE */ +} + +/*% + * #true returned if value was used + */ +static const char *single_dash_opts = "46dhimnruv"; +static const char *dash_opts = "46bcdfhikmnpqrtvyx"; +static bool +dash_option(char *option, char *next, dig_lookup_t **lookup, + bool *open_type_class, bool *need_clone, bool config_only, int argc, + char **argv, bool *firstarg) { + char opt, *value, *ptr, *ptr2, *ptr3, *last; + isc_result_t result; + bool value_from_next; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + char textname[MXNAME]; + struct in_addr in4; + struct in6_addr in6; + in_port_t srcport; + char *hash, *cmd; + uint32_t num; + + while (strpbrk(option, single_dash_opts) == &option[0]) { + /* + * Since the -[46dhimnuv] options do not take an argument, + * account for them (in any number and/or combination) + * if they appear as the first character(s) of a q-opt. + */ + opt = option[0]; + switch (opt) { + case '4': + if (have_ipv4) { + isc_net_disableipv6(); + have_ipv6 = false; + } else { + fatal("can't find IPv4 networking"); + UNREACHABLE(); + return (false); + } + break; + case '6': + if (have_ipv6) { + isc_net_disableipv4(); + have_ipv4 = false; + } else { + fatal("can't find IPv6 networking"); + UNREACHABLE(); + return (false); + } + break; + case 'd': + ptr = strpbrk(&option[1], dash_opts); + if (ptr != &option[1]) { + cmd = option; + FULLCHECK("debug"); + debugging = true; + return (false); + } else { + debugging = true; + } + break; + case 'h': + help(); + exit(0); + break; + case 'i': + /* deprecated */ + break; + case 'm': /* memdebug */ + /* memdebug is handled in preparse_args() */ + break; + case 'n': + /* deprecated */ + break; + case 'r': + debug("digrc (late)"); + digrc = false; + break; + case 'u': + (*lookup)->use_usec = true; + break; + case 'v': + version(); + exit(0); + break; + } + if (strlen(option) > 1U) { + option = &option[1]; + } else { + return (false); + } + } + opt = option[0]; + if (strlen(option) > 1U) { + value_from_next = false; + value = &option[1]; + } else { + value_from_next = true; + value = next; + } + if (value == NULL) { + goto invalid_option; + } + switch (opt) { + case 'b': + hash = strchr(value, '#'); + if (hash != NULL) { + result = parse_uint(&num, hash + 1, MAXPORT, + "port number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + srcport = num; + *hash = '\0'; + } else { + srcport = 0; + } + if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { + isc_sockaddr_fromin6(&localaddr, &in6, srcport); + isc_net_disableipv4(); + } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { + isc_sockaddr_fromin(&localaddr, &in4, srcport); + isc_net_disableipv6(); + } else { + if (hash != NULL) { + *hash = '#'; + } + fatal("invalid address %s", value); + } + if (hash != NULL) { + *hash = '#'; + } + specified_source = true; + return (value_from_next); + case 'c': + if ((*lookup)->rdclassset) { + fprintf(stderr, ";; Warning, extra class option\n"); + } + *open_type_class = false; + tr.base = value; + tr.length = (unsigned int)strlen(value); + result = dns_rdataclass_fromtext(&rdclass, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + (*lookup)->rdclass = rdclass; + (*lookup)->rdclassset = true; + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid class %s\n", + value); + } + return (value_from_next); + case 'f': + atomic_store(&batchname, (uintptr_t)value); + return (value_from_next); + case 'k': + strlcpy(keyfile, value, sizeof(keyfile)); + return (value_from_next); + case 'p': + result = parse_uint(&num, value, MAXPORT, "port number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + port = num; + port_set = true; + return (value_from_next); + case 'q': + if (!config_only) { + if (*need_clone) { + (*lookup) = clone_lookup(default_lookup, true); + } + *need_clone = true; + strlcpy((*lookup)->textname, value, + sizeof((*lookup)->textname)); + (*lookup)->trace_root = ((*lookup)->trace || + (*lookup)->ns_search_only); + (*lookup)->new_search = true; + if (*firstarg) { + printgreeting(argc, argv, *lookup); + *firstarg = false; + } + ISC_LIST_APPEND(lookup_list, (*lookup), link); + debug("looking up %s", (*lookup)->textname); + } + return (value_from_next); + case 't': + *open_type_class = false; + if (strncasecmp(value, "ixfr=", 5) == 0) { + rdtype = dns_rdatatype_ixfr; + result = ISC_R_SUCCESS; + } else { + tr.base = value; + tr.length = (unsigned int)strlen(value); + result = dns_rdatatype_fromtext( + &rdtype, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS && + rdtype == dns_rdatatype_ixfr) + { + result = DNS_R_UNKNOWN; + } + } + if (result == ISC_R_SUCCESS) { + if ((*lookup)->rdtypeset) { + fprintf(stderr, ";; Warning, " + "extra type option\n"); + } + if (rdtype == dns_rdatatype_ixfr) { + uint32_t serial; + (*lookup)->rdtype = dns_rdatatype_ixfr; + (*lookup)->rdtypeset = true; + result = parse_uint(&serial, &value[5], + MAXSERIAL, "serial number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse serial number"); + } + (*lookup)->ixfr_serial = serial; + (*lookup)->section_question = plusquest; + (*lookup)->comments = pluscomm; + if (!(*lookup)->tcp_mode_set) { + (*lookup)->tcp_mode = true; + } + } else { + (*lookup)->rdtype = rdtype; + if (!config_only) { + (*lookup)->rdtypeset = true; + } + if (rdtype == dns_rdatatype_axfr) { + (*lookup)->section_question = plusquest; + (*lookup)->comments = pluscomm; + } else if (rdtype == dns_rdatatype_any) { + if (!(*lookup)->tcp_mode_set) { + (*lookup)->tcp_mode = true; + } + } + (*lookup)->ixfr_serial = false; + } + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid type %s\n", + value); + } + return (value_from_next); + case 'y': + if ((ptr = strtok_r(value, ":", &last)) == NULL) { + usage(); + } + if ((ptr2 = strtok_r(NULL, ":", &last)) == NULL) { /* name or + * secret */ + usage(); + } + if ((ptr3 = strtok_r(NULL, ":", &last)) != NULL) { /* secret or + * NULL */ + parse_hmac(ptr); + ptr = ptr2; + ptr2 = ptr3; + } else { + hmacname = DNS_TSIG_HMACMD5_NAME; + digestbits = 0; + } + /* XXXONDREJ: FIXME */ + strlcpy(keynametext, ptr, sizeof(keynametext)); + strlcpy(keysecret, ptr2, sizeof(keysecret)); + return (value_from_next); + case 'x': + if (*need_clone) { + *lookup = clone_lookup(default_lookup, true); + } + *need_clone = true; + if (get_reverse(textname, sizeof(textname), value, false) == + ISC_R_SUCCESS) + { + strlcpy((*lookup)->textname, textname, + sizeof((*lookup)->textname)); + debug("looking up %s", (*lookup)->textname); + (*lookup)->trace_root = ((*lookup)->trace || + (*lookup)->ns_search_only); + if (!(*lookup)->rdtypeset) { + (*lookup)->rdtype = dns_rdatatype_ptr; + } + if (!(*lookup)->rdclassset) { + (*lookup)->rdclass = dns_rdataclass_in; + } + (*lookup)->new_search = true; + if (*firstarg) { + printgreeting(argc, argv, *lookup); + *firstarg = false; + } + ISC_LIST_APPEND(lookup_list, *lookup, link); + } else { + fprintf(stderr, "Invalid IP address %s\n", value); + exit(1); + } + return (value_from_next); + invalid_option: + default: + fprintf(stderr, "Invalid option: -%s\n", option); + usage(); + } + UNREACHABLE(); + return (false); +} + +/*% + * Because we may be trying to do memory allocation recording, we're going + * to need to parse the arguments for the -m *before* we start the main + * argument parsing routine. + * + * I'd prefer not to have to do this, but I am not quite sure how else to + * fix the problem. Argument parsing in dig involves memory allocation + * by its nature, so it can't be done in the main argument parser. + */ +static void +preparse_args(int argc, char **argv) { + int rc; + char **rv; + char *option; + + rc = argc; + rv = argv; + for (rc--, rv++; rc > 0; rc--, rv++) { + if (rv[0][0] != '-') { + continue; + } + option = &rv[0][1]; + while (strpbrk(option, single_dash_opts) == &option[0]) { + switch (option[0]) { + case 'd': + /* For debugging early startup */ + debugging = true; + break; + case 'm': + memdebugging = true; + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + case 'r': + /* + * Must be done early, because ~/.digrc + * is read before command line parsing + */ + debug("digrc (early)"); + digrc = false; + break; + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + } + option = &option[1]; + } + if (strlen(option) == 0U) { + continue; + } + /* Look for dash value option. */ + if (strpbrk(option, dash_opts) != &option[0]) { + goto invalid_option; + } + if (strlen(option) > 1U) { + /* value in option. */ + continue; + } + /* Dash value is next argument so we need to skip it. */ + rc--, rv++; + /* Handle missing argument */ + if (rc == 0) { + invalid_option: + fprintf(stderr, "Invalid option: -%s\n", option); + usage(); + } + } +} + +static int +split_batchline(char *batchline, char **bargv, int len, const char *msg) { + int bargc; + char *last = NULL; + + REQUIRE(batchline != NULL); + + for (bargc = 1, bargv[bargc] = strtok_r(batchline, " \t\r\n", &last); + bargc < len && bargv[bargc]; + bargv[++bargc] = strtok_r(NULL, " \t\r\n", &last)) + { + debug("%s %d: %s", msg, bargc, bargv[bargc]); + } + return (bargc); +} + +static void +parse_args(bool is_batchfile, bool config_only, int argc, char **argv) { + isc_result_t result; + isc_textregion_t tr; + bool firstarg = true; + dig_lookup_t *lookup = NULL; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + bool open_type_class = true; + char batchline[MXNAME]; + int bargc; + char *bargv[64]; + int rc; + char **rv; +#ifndef NOPOSIX + char *homedir; + char rcfile[PATH_MAX]; +#endif /* ifndef NOPOSIX */ + bool need_clone = true; + + /* + * The semantics for parsing the args is a bit complex; if + * we don't have a host yet, make the arg apply globally, + * otherwise make it apply to the latest host. This is + * a bit different than the previous versions, but should + * form a consistent user interface. + * + * First, create a "default lookup" which won't actually be used + * anywhere, except for cloning into new lookups + */ + + debug("parse_args()"); + if (!is_batchfile) { + debug("making new lookup"); + default_lookup = make_empty_lookup(); + default_lookup->adflag = true; + default_lookup->edns = DEFAULT_EDNS_VERSION; + default_lookup->sendcookie = true; + +#ifndef NOPOSIX + /* + * Treat ${HOME}/.digrc as a special batchfile + */ + INSIST(batchfp == NULL); + homedir = getenv("HOME"); + if (homedir != NULL && digrc) { + unsigned int n; + debug("digrc (open)"); + n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc", + homedir); + if (n < sizeof(rcfile)) { + batchfp = fopen(rcfile, "r"); + } + } + if (batchfp != NULL) { + while (fgets(batchline, sizeof(batchline), batchfp) != + 0) + { + debug("config line %s", batchline); + bargc = split_batchline(batchline, bargv, 62, + ".digrc argv"); + bargv[0] = argv[0]; + argv0 = argv[0]; + parse_args(true, true, bargc, (char **)bargv); + } + fclose(batchfp); + } +#endif /* ifndef NOPOSIX */ + } + + if (is_batchfile && !config_only) { + /* Processing '-f batchfile'. */ + lookup = clone_lookup(default_lookup, true); + need_clone = false; + } else { + lookup = default_lookup; + } + + rc = argc; + rv = argv; + for (rc--, rv++; rc > 0; rc--, rv++) { + debug("main parsing %s", rv[0]); + if (strncmp(rv[0], "%", 1) == 0) { + break; + } + if (rv[0][0] == '@') { + if (is_batchfile && !config_only) { + addresscount = getaddresses(lookup, &rv[0][1], + &result); + if (addresscount == 0) { + fprintf(stderr, + "couldn't get address " + "for '%s': %s: skipping " + "lookup\n", + &rv[0][1], + isc_result_totext(result)); + if (ISC_LINK_LINKED(lookup, link)) { + ISC_LIST_DEQUEUE(lookup_list, + lookup, link); + } + destroy_lookup(lookup); + return; + } + } else { + addresscount = getaddresses(lookup, &rv[0][1], + NULL); + if (addresscount == 0) { + fatal("no valid addresses for '%s'\n", + &rv[0][1]); + } + } + } else if (rv[0][0] == '+') { + lookup = plus_option(&rv[0][1], is_batchfile, + &need_clone, lookup); + } else if (rv[0][0] == '-') { + if (rc <= 1) { + if (dash_option(&rv[0][1], NULL, &lookup, + &open_type_class, &need_clone, + config_only, argc, argv, + &firstarg)) + { + rc--; + rv++; + } + } else { + if (dash_option(&rv[0][1], rv[1], &lookup, + &open_type_class, &need_clone, + config_only, argc, argv, + &firstarg)) + { + rc--; + rv++; + } + } + } else { + /* + * Anything which isn't an option + */ + if (open_type_class) { + if (strncasecmp(rv[0], "ixfr=", 5) == 0) { + rdtype = dns_rdatatype_ixfr; + result = ISC_R_SUCCESS; + } else { + tr.base = rv[0]; + tr.length = (unsigned int)strlen(rv[0]); + result = dns_rdatatype_fromtext( + &rdtype, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS && + rdtype == dns_rdatatype_ixfr) + { + fprintf(stderr, ";; Warning, " + "ixfr requires " + "a " + "serial " + "number\n"); + continue; + } + } + if (result == ISC_R_SUCCESS) { + if (lookup->rdtypeset) { + fprintf(stderr, ";; Warning, " + "extra type " + "option\n"); + } + if (rdtype == dns_rdatatype_ixfr) { + uint32_t serial; + lookup->rdtype = + dns_rdatatype_ixfr; + lookup->rdtypeset = true; + result = parse_uint(&serial, + &rv[0][5], + MAXSERIAL, + "serial " + "number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse " + "serial number"); + } + lookup->ixfr_serial = serial; + lookup->section_question = + plusquest; + lookup->comments = pluscomm; + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = true; + } + } else { + lookup->rdtype = rdtype; + lookup->rdtypeset = true; + if (rdtype == + dns_rdatatype_axfr) + { + lookup->section_question = + plusquest; + lookup->comments = + pluscomm; + } + if (rdtype == + dns_rdatatype_any && + !lookup->tcp_mode_set) + { + lookup->tcp_mode = true; + } + lookup->ixfr_serial = false; + } + continue; + } + result = dns_rdataclass_fromtext( + &rdclass, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + if (lookup->rdclassset) { + fprintf(stderr, ";; Warning, " + "extra class " + "option\n"); + } + lookup->rdclass = rdclass; + lookup->rdclassset = true; + continue; + } + } + + if (!config_only) { + if (need_clone) { + lookup = clone_lookup(default_lookup, + true); + } + need_clone = true; + strlcpy(lookup->textname, rv[0], + sizeof(lookup->textname)); + lookup->trace_root = (lookup->trace || + lookup->ns_search_only); + lookup->new_search = true; + if (firstarg) { + printgreeting(argc, argv, lookup); + firstarg = false; + } + ISC_LIST_APPEND(lookup_list, lookup, link); + debug("looking up %s", lookup->textname); + } + /* XXX Error message */ + } + } + + /* + * If we have a batchfile, seed the lookup list with the + * first entry, then trust the callback in dighost_shutdown + * to get the rest + */ + char *filename = (char *)atomic_load(&batchname); + if ((filename != NULL) && !(is_batchfile)) { + if (strcmp(filename, "-") == 0) { + batchfp = stdin; + } else { + batchfp = fopen(filename, "r"); + } + if (batchfp == NULL) { + perror(filename); + if (exitcode < 8) { + exitcode = 8; + } + fatal("couldn't open specified batch file"); + } + /* XXX Remove code dup from shutdown code */ + next_line: + if (fgets(batchline, sizeof(batchline), batchfp) != 0) { + debug("batch line %s", batchline); + if (batchline[0] == '\r' || batchline[0] == '\n' || + batchline[0] == '#' || batchline[0] == ';') + { + goto next_line; + } + bargc = split_batchline(batchline, bargv, 14, + "batch argv"); + bargv[0] = argv[0]; + argv0 = argv[0]; + parse_args(true, false, bargc, (char **)bargv); + return; + } + return; + } + /* + * If no lookup specified, search for root + */ + if ((lookup_list.head == NULL) && !config_only) { + if (need_clone) { + lookup = clone_lookup(default_lookup, true); + } + need_clone = true; + lookup->trace_root = (lookup->trace || lookup->ns_search_only); + lookup->new_search = true; + strlcpy(lookup->textname, ".", sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + if (firstarg) { + printgreeting(argc, argv, lookup); + firstarg = false; + } + ISC_LIST_APPEND(lookup_list, lookup, link); + } + if (!need_clone) { + destroy_lookup(lookup); + } +} + +/* + * Callback from dighost.c to allow program-specific shutdown code. + * Here, we're possibly reading from a batch file, then shutting down + * for real if there's nothing in the batch file to read. + */ +static void +query_finished(void) { + char batchline[MXNAME]; + int bargc; + char *bargv[16]; + + if (atomic_load(&batchname) == 0) { + isc_app_shutdown(); + return; + } + + fflush(stdout); + if (feof(batchfp)) { + atomic_store(&batchname, 0); + isc_app_shutdown(); + if (batchfp != stdin) { + fclose(batchfp); + } + return; + } + + if (fgets(batchline, sizeof(batchline), batchfp) != 0) { + debug("batch line %s", batchline); + bargc = split_batchline(batchline, bargv, 14, "batch argv"); + bargv[0] = argv0; + parse_args(true, false, bargc, (char **)bargv); + start_lookup(); + } else { + atomic_store(&batchname, 0); + if (batchfp != stdin) { + fclose(batchfp); + } + isc_app_shutdown(); + return; + } +} + +static void +dig_error(const char *format, ...) { + va_list args; + + if (yaml) { + printf("-\n"); + printf(" type: DIG_ERROR\n"); + + /* + * Print an indent before a literal block quote. + * Note: this will break if used to print more than + * one line of text as only the first line would be + * indented. + */ + printf(" message: |\n"); + printf(" "); + } else { + printf(";; "); + } + + va_start(args, format); + vprintf(format, args); + va_end(args); + + if (!yaml) { + printf("\n"); + } +} + +static void +dig_warning(const char *format, ...) { + va_list args; + + if (!yaml) { + printf(";; "); + + va_start(args, format); + vprintf(format, args); + va_end(args); + + printf("\n"); + } +} + +static void +dig_comments(dig_lookup_t *lookup, const char *format, ...) { + va_list args; + + if (lookup->comments && !yaml) { + printf(";; "); + + va_start(args, format); + vprintf(format, args); + va_end(args); + + printf("\n"); + } +} + +void +dig_setup(int argc, char **argv) { + isc_result_t result; + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + debug("dig_setup()"); + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = query_finished; + dighost_error = dig_error; + dighost_warning = dig_warning; + dighost_comments = dig_comments; + + progname = argv[0]; + preparse_args(argc, argv); + + result = isc_app_start(); + check_result(result, "isc_app_start"); + + setup_libs(); + setup_system(ipv4only, ipv6only); +} + +void +dig_query_setup(bool is_batchfile, bool config_only, int argc, char **argv) { + debug("dig_query_setup"); + + parse_args(is_batchfile, config_only, argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + if (domainopt[0] != '\0') { + set_search_domain(domainopt); + usesearch = true; + } +} + +void +dig_startup(void) { + isc_result_t result; + + debug("dig_startup()"); + + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + check_result(result, "isc_app_onrun"); + isc_app_run(); +} + +void +dig_query_start(void) { + start_lookup(); +} + +void +dig_shutdown(void) { + destroy_lookup(default_lookup); + if (atomic_load(&batchname) != 0) { + if (batchfp != stdin) { + fclose(batchfp); + } + atomic_store(&batchname, 0); + } + cancel_all(); + destroy_libs(); + isc_app_finish(); +} + +/*% Main processing routine for dig */ +int +main(int argc, char **argv) { + dig_setup(argc, argv); + dig_query_setup(false, false, argc, argv); + dig_startup(); + dig_shutdown(); + + return (exitcode); +} diff --git a/bin/dig/dig.rst b/bin/dig/dig.rst new file mode 100644 index 0000000..59ac9f1 --- /dev/null +++ b/bin/dig/dig.rst @@ -0,0 +1,809 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dig +.. program:: dig +.. _man_dig: + +dig - DNS lookup utility +------------------------ + +Synopsis +~~~~~~~~ +:program:`dig` [@server] [**-b** address] [**-c** class] [**-f** filename] [**-k** filename] [**-m**] [**-p** port#] [**-q** name] [**-t** type] [**-v**] [**-x** addr] [**-y** [hmac:]name:key] [ [**-4**] | [**-6**] ] [name] [type] [class] [queryopt...] + +:program:`dig` [**-h**] + +:program:`dig` [global-queryopt...] [query...] + +Description +~~~~~~~~~~~ + +:program:`dig` is a flexible tool for interrogating DNS name servers. It +performs DNS lookups and displays the answers that are returned from the +name server(s) that were queried. Most DNS administrators use :program:`dig` to +troubleshoot DNS problems because of its flexibility, ease of use, and +clarity of output. Other lookup tools tend to have less functionality +than :program:`dig`. + +Although :program:`dig` is normally used with command-line arguments, it also +has a batch mode of operation for reading lookup requests from a file. A +brief summary of its command-line arguments and options is printed when +the :option:`-h` option is given. The BIND 9 +implementation of :program:`dig` allows multiple lookups to be issued from the +command line. + +Unless it is told to query a specific name server, :program:`dig` tries each +of the servers listed in ``/etc/resolv.conf``. If no usable server +addresses are found, :program:`dig` sends the query to the local host. + +When no command-line arguments or options are given, :program:`dig` +performs an NS query for "." (the root). + +It is possible to set per-user defaults for :program:`dig` via +``${HOME}/.digrc``. This file is read and any options in it are applied +before the command-line arguments. The :option:`-r` option disables this +feature, for scripts that need predictable behavior. + +The IN and CH class names overlap with the IN and CH top-level domain +names. Either use the :option:`-t` and :option:`-c` options to specify the type and +class, use the :option:`-q` to specify the domain name, or use "IN." and +"CH." when looking up these top-level domains. + +Simple Usage +~~~~~~~~~~~~ + +A typical invocation of :program:`dig` looks like: + +:: + + dig @server name type + +where: + +.. option:: server + + is the name or IP address of the name server to query. This can be an + IPv4 address in dotted-decimal notation or an IPv6 address in + colon-delimited notation. When the supplied ``server`` argument is a + hostname, :program:`dig` resolves that name before querying that name + server. + + If no ``server`` argument is provided, :program:`dig` consults + ``/etc/resolv.conf``; if an address is found there, it queries the + name server at that address. If either of the :option:`-4` or :option:`-6` + options are in use, then only addresses for the corresponding + transport are tried. If no usable addresses are found, :program:`dig` + sends the query to the local host. The reply from the name server + that responds is displayed. + +.. option:: name + + is the name of the resource record that is to be looked up. + +.. option:: type + + indicates what type of query is required - ANY, A, MX, SIG, etc. + ``type`` can be any valid query type. If no ``type`` argument is + supplied, :program:`dig` performs a lookup for an A record. + +Options +~~~~~~~ + +.. option:: -4 + + This option indicates that only IPv4 should be used. + +.. option:: -6 + + This option indicates that only IPv6 should be used. + +.. option:: -b address[#port] + + This option sets the source IP address of the query. The ``address`` must be a + valid address on one of the host's network interfaces, or "0.0.0.0" + or "::". An optional port may be specified by appending ``#port``. + +.. option:: -c class + + This option sets the query class. The default ``class`` is IN; other classes are + HS for Hesiod records or CH for Chaosnet records. + +.. option:: -f file + + This option sets batch mode, in which :program:`dig` reads a list of lookup requests to process from + the given ``file``. Each line in the file should be organized in the + same way it would be presented as a query to :program:`dig` using the + command-line interface. + +.. option:: -h + + Print a usage summary. + +.. option:: -k keyfile + + This option tells :program:`dig` to sign queries using TSIG or + SIG(0) using a key read from the given file. Key files can be + generated using :iscman:`tsig-keygen`. When using TSIG authentication + with :program:`dig`, the name server that is queried needs to + know the key and algorithm that is being used. In BIND, this is + done by providing appropriate ``key`` and ``server`` statements + in :iscman:`named.conf` for TSIG and by looking up the KEY record + in zone data for SIG(0). + +.. option:: -m + + This option enables memory usage debugging. + +.. option:: -p port + + This option sends the query to a non-standard port on the server, instead of the + default port 53. This option is used to test a name server that + has been configured to listen for queries on a non-standard port + number. + +.. option:: -q name + + This option specifies the domain name to query. This is useful to distinguish the ``name`` + from other arguments. + +.. option:: -r + + This option indicates that options from ``${HOME}/.digrc`` should not be read. This is useful for + scripts that need predictable behavior. + +.. option:: -t type + + This option indicates the resource record type to query, which can be any valid query type. If + it is a resource record type supported in BIND 9, it can be given by + the type mnemonic (such as ``NS`` or ``AAAA``). The default query type is + ``A``, unless the :option:`-x` option is supplied to indicate a reverse + lookup. A zone transfer can be requested by specifying a type of + AXFR. When an incremental zone transfer (IXFR) is required, set the + ``type`` to ``ixfr=N``. The incremental zone transfer contains + all changes made to the zone since the serial number in the zone's + SOA record was ``N``. + + All resource record types can be expressed as ``TYPEnn``, where ``nn`` is + the number of the type. If the resource record type is not supported + in BIND 9, the result is displayed as described in :rfc:`3597`. + +.. option:: -u + + This option indicates that print query times should be provided in microseconds instead of milliseconds. + +.. option:: -v + + This option prints the version number and exits. + +.. option:: -x addr + + This option sets simplified reverse lookups, for mapping addresses to names. The + ``addr`` is an IPv4 address in dotted-decimal notation, or a + colon-delimited IPv6 address. When the :option:`-x` option is used, there is no + need to provide the ``name``, ``class``, and ``type`` arguments. + :program:`dig` automatically performs a lookup for a name like + ``94.2.0.192.in-addr.arpa`` and sets the query type and class to PTR + and IN respectively. IPv6 addresses are looked up using nibble format + under the IP6.ARPA domain. + +.. option:: -y [hmac:]keyname:secret + + This option signs queries using TSIG with the given authentication key. + ``keyname`` is the name of the key, and ``secret`` is the + base64-encoded shared secret. ``hmac`` is the name of the key algorithm; + valid choices are ``hmac-md5``, ``hmac-sha1``, ``hmac-sha224``, + ``hmac-sha256``, ``hmac-sha384``, or ``hmac-sha512``. If ``hmac`` is + not specified, the default is ``hmac-md5``; if MD5 was disabled, the default is + ``hmac-sha256``. + +.. note:: Only the :option:`-k` option should be used, rather than the :option:`-y` option, + because with :option:`-y` the shared secret is supplied as a command-line + argument in clear text. This may be visible in the output from ``ps1`` or + in a history file maintained by the user's shell. + +Query Options +~~~~~~~~~~~~~ + +:program:`dig` provides a number of query options which affect the way in which +lookups are made and the results displayed. Some of these set or reset +flag bits in the query header, some determine which sections of the +answer get printed, and others determine the timeout and retry +strategies. + +Each query option is identified by a keyword preceded by a plus sign +(``+``). Some keywords set or reset an option; these may be preceded by +the string ``no`` to negate the meaning of that keyword. Other keywords +assign values to options, like the timeout interval. They have the form +``+keyword=value``. Keywords may be abbreviated, provided the +abbreviation is unambiguous; for example, :option:`+cd` is equivalent to +:option:`+cdflag`. The query options are: + +.. option:: +aaflag, +noaaflag + + This option is a synonym for :option:`+aaonly`, :option:`+noaaonly`. + +.. option:: +aaonly, +noaaonly + + This option sets the ``aa`` flag in the query. + +.. option:: +additional, +noadditional + + This option displays [or does not display] the additional section of a reply. The + default is to display it. + +.. option:: +adflag, +noadflag + + This option sets [or does not set] the AD (authentic data) bit in the query. This + requests the server to return whether all of the answer and authority + sections have been validated as secure, according to the security + policy of the server. ``AD=1`` indicates that all records have been + validated as secure and the answer is not from a OPT-OUT range. ``AD=0`` + indicates that some part of the answer was insecure or not validated. + This bit is set by default. + +.. option:: +all, +noall + + This option sets or clears all display flags. + +.. option:: +answer, +noanswer + + This option displays [or does not display] the answer section of a reply. The default + is to display it. + +.. option:: +authority, +noauthority + + This option displays [or does not display] the authority section of a reply. The + default is to display it. + +.. option:: +badcookie, +nobadcookie + + This option retries the lookup with a new server cookie if a BADCOOKIE response is + received. + +.. option:: +besteffort, +nobesteffort + + This option attempts to display the contents of messages which are malformed. The + default is to not display malformed answers. + +.. option:: +bufsize[=B] + + This option sets the UDP message buffer size advertised using EDNS0 to + ``B`` bytes. The maximum and minimum sizes of this buffer are 65535 and + 0, respectively. ``+bufsize`` restores the default buffer size. + +.. option:: +cd, +cdflag, +nocdflag + + This option sets [or does not set] the CD (checking disabled) bit in the query. This + requests the server to not perform DNSSEC validation of responses. + +.. option:: +class, +noclass + + This option displays [or does not display] the CLASS when printing the record. + +.. option:: +cmd, +nocmd + + This option toggles the printing of the initial comment in the output, identifying the + version of :program:`dig` and the query options that have been applied. This option + always has a global effect; it cannot be set globally and then overridden on a + per-lookup basis. The default is to print this comment. + +.. option:: +comments, +nocomments + + This option toggles the display of some comment lines in the output, with + information about the packet header and OPT pseudosection, and the names of + the response section. The default is to print these comments. + + Other types of comments in the output are not affected by this option, but + can be controlled using other command-line switches. These include + :option:`+cmd`, :option:`+question`, :option:`+stats`, and :option:`+rrcomments`. + +.. option:: +cookie=####, +nocookie + + This option sends [or does not send] a COOKIE EDNS option, with an optional value. Replaying a COOKIE + from a previous response allows the server to identify a previous + client. The default is ``+cookie``. + + ``+cookie`` is also set when :option:`+trace` is set to better emulate the + default queries from a nameserver. + +.. option:: +crypto, +nocrypto + + This option toggles the display of cryptographic fields in DNSSEC records. The + contents of these fields are unnecessary for debugging most DNSSEC + validation failures and removing them makes it easier to see the + common failures. The default is to display the fields. When omitted, + they are replaced by the string ``[omitted]`` or, in the DNSKEY case, the + key ID is displayed as the replacement, e.g. ``[ key id = value ]``. + +.. option:: +defname, +nodefname + + This option, which is deprecated, is treated as a synonym for + :option:`+search`, :option:`+nosearch`. + +.. option:: +dns64prefix, +nodns64prefix + + Lookup IPV4ONLY.ARPA AAAA and print any DNS64 prefixes found. + +.. option:: +dnssec, +do, +nodnssec, +nodo + + This option requests that DNSSEC records be sent by setting the DNSSEC OK (DO) bit in + the OPT record in the additional section of the query. + +.. option:: +domain=somename + + This option sets the search list to contain the single domain ``somename``, as if + specified in a ``domain`` directive in ``/etc/resolv.conf``, and + enables search list processing as if the :option:`+search` option were + given. + +.. option:: +dscp=value + + This option formerly set the DSCP value used when sending a query. + It is now obsolete, and has no effect. + +.. option:: +edns[=#], +noedns + + This option specifies the EDNS version to query with. Valid values are 0 to 255. + Setting the EDNS version causes an EDNS query to be sent. + ``+noedns`` clears the remembered EDNS version. EDNS is set to 0 by + default. + +.. option:: +ednsflags[=#], +noednsflags + + This option sets the must-be-zero EDNS flags bits (Z bits) to the specified value. + Decimal, hex, and octal encodings are accepted. Setting a named flag + (e.g., DO) is silently ignored. By default, no Z bits are set. + +.. option:: +ednsnegotiation, +noednsnegotiation + + This option enables/disables EDNS version negotiation. By default, EDNS version + negotiation is enabled. + +.. option:: +ednsopt[=code[:value]], +noednsopt + + This option specifies the EDNS option with code point ``code`` and an optional payload + of ``value`` as a hexadecimal string. ``code`` can be either an EDNS + option name (for example, ``NSID`` or ``ECS``) or an arbitrary + numeric value. ``+noednsopt`` clears the EDNS options to be sent. + +.. option:: +expire, +noexpire + + This option sends an EDNS Expire option. + +.. option:: +fail, +nofail + + This option indicates that :iscman:`named` should try [or not try] the next server if a SERVFAIL is received. The default is + to not try the next server, which is the reverse of normal stub + resolver behavior. + +.. option:: +fuzztime[=value], +nofuzztime + + This option allows the signing time to be specified when generating + signed messages. If a value is specified it is the seconds since + 00:00:00 January 1, 1970 UTC ignoring leap seconds. If no value + is specified 1646972129 (Fri 11 Mar 2022 04:15:29 UTC) is used. + The default is ``+nofuzztime`` and the current time is used. + +.. option:: +header-only, +noheader-only + + This option sends a query with a DNS header without a question section. The + default is to add a question section. The query type and query name + are ignored when this is set. + +.. option:: +https[=value], +nohttps + + This option indicates whether to use DNS over HTTPS (DoH) when querying + name servers. When this option is in use, the port number defaults to 443. + The HTTP POST request mode is used when sending the query. + + If ``value`` is specified, it will be used as the HTTP endpoint in the + query URI; the default is ``/dns-query``. So, for example, ``dig + @example.com +https`` will use the URI ``https://example.com/dns-query``. + +.. option:: +https-get[=value], +nohttps-get + + Similar to :option:`+https`, except that the HTTP GET request mode is used + when sending the query. + +.. option:: +https-post[=value], +nohttps-post + + Same as :option:`+https`. + +.. option:: +http-plain[=value], +nohttp-plain + + Similar to :option:`+https`, except that HTTP queries will be sent over a + non-encrypted channel. When this option is in use, the port number + defaults to 80 and the HTTP request mode is POST. + +.. option:: +http-plain-get[=value], +nohttp-plain-get + + Similar to :option:`+http-plain`, except that the HTTP request mode is GET. + +.. option:: +http-plain-post[=value], +nohttp-plain-post + + Same as :option:`+http-plain`. + +.. option:: +identify, +noidentify + + This option shows [or does not show] the IP address and port number that + supplied the answer, when the :option:`+short` option is enabled. If short + form answers are requested, the default is not to show the source + address and port number of the server that provided the answer. + +.. option:: +idnin, +noidnin + + This option processes [or does not process] IDN domain names on input. This requires + ``IDN SUPPORT`` to have been enabled at compile time. + + The default is to process IDN input when standard output is a tty. + The IDN processing on input is disabled when :program:`dig` output is redirected + to files, pipes, and other non-tty file descriptors. + +.. option:: +idnout, +noidnout + + This option converts [or does not convert] puny code on output. This requires + ``IDN SUPPORT`` to have been enabled at compile time. + + The default is to process puny code on output when standard output is + a tty. The puny code processing on output is disabled when :program:`dig` output + is redirected to files, pipes, and other non-tty file descriptors. + +.. option:: +ignore, +noignore + + This option ignores [or does not ignore] truncation in UDP responses instead of retrying with TCP. By + default, TCP retries are performed. + +.. option:: +keepalive, +nokeepalive + + This option sends [or does not send] an EDNS Keepalive option. + +.. option:: +keepopen, +nokeepopen + + This option keeps [or does not keep] the TCP socket open between queries, and reuses it rather than + creating a new TCP socket for each lookup. The default is + ``+nokeepopen``. + +.. option:: +multiline, +nomultiline + + This option prints [or does not print] records, like the SOA records, in a verbose multi-line format + with human-readable comments. The default is to print each record on + a single line to facilitate machine parsing of the :program:`dig` output. + +.. option:: +ndots=D + + This option sets the number of dots (``D``) that must appear in ``name`` for + it to be considered absolute. The default value is that defined using + the ``ndots`` statement in ``/etc/resolv.conf``, or 1 if no ``ndots`` + statement is present. Names with fewer dots are interpreted as + relative names, and are searched for in the domains listed in the + ``search`` or ``domain`` directive in ``/etc/resolv.conf`` if + :option:`+search` is set. + +.. option:: +nsid, +nonsid + + When enabled, this option includes an EDNS name server ID request when sending a query. + +.. option:: +nssearch, +nonssearch + + When this option is set, :program:`dig` attempts to find the authoritative + name servers for the zone containing the name being looked up, and + display the SOA record that each name server has for the zone. + Addresses of servers that did not respond are also printed. + +.. option:: +onesoa, +noonesoa + + When enabled, this option prints only one (starting) SOA record when performing an AXFR. The + default is to print both the starting and ending SOA records. + +.. option:: +opcode=value, +noopcode + + When enabled, this option sets (restores) the DNS message opcode to the specified value. The + default value is QUERY (0). + +.. option:: +padding=value + + This option pads the size of the query packet using the EDNS Padding option to + blocks of ``value`` bytes. For example, ``+padding=32`` causes a + 48-byte query to be padded to 64 bytes. The default block size is 0, + which disables padding; the maximum is 512. Values are ordinarily + expected to be powers of two, such as 128; however, this is not + mandatory. Responses to padded queries may also be padded, but only + if the query uses TCP or DNS COOKIE. + +.. option:: +qid=value + + This option specifies the query ID to use when sending queries. + +.. option:: +qr, +noqr + + This option toggles the display of the query message as it is sent. By default, the query + is not printed. + +.. option:: +question, +noquestion + + This option toggles the display of the question section of a query when an answer is + returned. The default is to print the question section as a comment. + +.. option:: +raflag, +noraflag + + This option sets [or does not set] the RA (Recursion Available) bit in the query. The + default is ``+noraflag``. This bit is ignored by the server for + QUERY. + +.. option:: +rdflag, +nordflag + + This option is a synonym for :option:`+recurse`, :option:`+norecurse`. + +.. option:: +recurse, +norecurse + + This option toggles the setting of the RD (recursion desired) bit in the query. + This bit is set by default, which means :program:`dig` normally sends + recursive queries. Recursion is automatically disabled when the + :option:`+nssearch` or :option:`+trace` query option is used. + +.. option:: +retry=T + + This option sets the number of times to retry UDP and TCP queries to server to ``T`` + instead of the default, 2. Unlike :option:`+tries`, this does not include + the initial query. + +.. option:: +rrcomments, +norrcomments + + This option toggles the display of per-record comments in the output (for example, + human-readable key information about DNSKEY records). The default is + not to print record comments unless multiline mode is active. + +.. option:: +search, +nosearch + + This option uses [or does not use] the search list defined by the searchlist or domain + directive in ``resolv.conf``, if any. The search list is not used by + default. + + ``ndots`` from ``resolv.conf`` (default 1), which may be overridden by + :option:`+ndots`, determines whether the name is treated as relative + and hence whether a search is eventually performed. + +.. option:: +short, +noshort + + This option toggles whether a terse answer is provided. The default is to print the answer in a verbose + form. This option always has a global effect; it cannot be set globally and + then overridden on a per-lookup basis. + +.. option:: +showbadcookie, +noshowbadcookie + + This option toggles whether to show the message containing the + BADCOOKIE rcode before retrying the request or not. The default + is to not show the messages. + +.. option:: +showsearch, +noshowsearch + + This option performs [or does not perform] a search showing intermediate results. + +.. option:: +sigchase, +nosigchase + + This feature is now obsolete and has been removed; use :iscman:`delv` + instead. + +.. option:: +split=W + + This option splits long hex- or base64-formatted fields in resource records into + chunks of ``W`` characters (where ``W`` is rounded up to the nearest + multiple of 4). ``+nosplit`` or ``+split=0`` causes fields not to be + split at all. The default is 56 characters, or 44 characters when + multiline mode is active. + +.. option:: +stats, +nostats + + This option toggles the printing of statistics: when the query was made, the size of the + reply, etc. The default behavior is to print the query statistics as a + comment after each lookup. + +.. option:: +subnet=addr[/prefix-length], +nosubnet + + This option sends [or does not send] an EDNS CLIENT-SUBNET option with the specified IP + address or network prefix. + + ``dig +subnet=0.0.0.0/0``, or simply ``dig +subnet=0`` for short, + sends an EDNS CLIENT-SUBNET option with an empty address and a source + prefix-length of zero, which signals a resolver that the client's + address information must *not* be used when resolving this query. + +.. option:: +tcflag, +notcflag + + This option sets [or does not set] the TC (TrunCation) bit in the query. The default is + ``+notcflag``. This bit is ignored by the server for QUERY. + +.. option:: +tcp, +notcp + + This option indicates whether to use TCP when querying name + servers. The default behavior is to use UDP unless a type ``any`` + or ``ixfr=N`` query is requested, in which case the default is + TCP. AXFR queries always use TCP. To prevent retry over TCP when + TC=1 is returned from a UDP query, use ``+ignore``. + +.. option:: +timeout=T + + This option sets the timeout for a query to ``T`` seconds. The default timeout is + 5 seconds. An attempt to set ``T`` to less than 1 is silently set to 1. + +.. option:: +tls, +notls + + This option indicates whether to use DNS over TLS (DoT) when querying + name servers. When this option is in use, the port number defaults + to 853. + +.. option:: +tls-ca[=file-name], +notls-ca + + This option enables remote server TLS certificate validation for + DNS transports, relying on TLS. Certificate authorities + certificates are loaded from the specified PEM file + (``file-name``). If the file is not specified, the default + certificates from the global certificates store are used. + +.. option:: +tls-certfile=file-name, +tls-keyfile=file-name, +notls-certfile, +notls-keyfile + + These options set the state of certificate-based client + authentication for DNS transports, relying on TLS. Both certificate + chain file and private key file are expected to be in PEM format. + Both options must be specified at the same time. + +.. option:: +tls-hostname=hostname, +notls-hostname + + This option makes :program:`dig` use the provided hostname during remote + server TLS certificate verification. Otherwise, the DNS server name + is used. This option has no effect if :option:`+tls-ca` is not specified. + +.. option:: +topdown, +notopdown + + This feature is related to :option:`dig +sigchase`, which is obsolete and + has been removed. Use :iscman:`delv` instead. + +.. option:: +trace, +notrace + + This option toggles tracing of the delegation path from the root name servers for + the name being looked up. Tracing is disabled by default. When + tracing is enabled, :program:`dig` makes iterative queries to resolve the + name being looked up. It follows referrals from the root servers, + showing the answer from each server that was used to resolve the + lookup. + + If ``@server`` is also specified, it affects only the initial query for + the root zone name servers. + + :option:`+dnssec` is also set when :option:`+trace` is set, to better emulate the + default queries from a name server. + +.. option:: +tries=T + + This option sets the number of times to try UDP and TCP queries to server to ``T`` + instead of the default, 3. If ``T`` is less than or equal to zero, + the number of tries is silently rounded up to 1. + +.. option:: +trusted-key=#### + + This option formerly specified trusted keys for use with :option:`dig +sigchase`. This + feature is now obsolete and has been removed; use :iscman:`delv` instead. + +.. option:: +ttlid, +nottlid + + This option displays [or does not display] the TTL when printing the record. + +.. option:: +ttlunits, +nottlunits + + This option displays [or does not display] the TTL in friendly human-readable time + units of ``s``, ``m``, ``h``, ``d``, and ``w``, representing seconds, minutes, + hours, days, and weeks. This implies :option:`+ttlid`. + +.. option:: +unknownformat, +nounknownformat + + This option prints all RDATA in unknown RR type presentation format (:rfc:`3597`). + The default is to print RDATA for known types in the type's + presentation format. + +.. option:: +vc, +novc + + This option uses [or does not use] TCP when querying name servers. This alternate + syntax to :option:`+tcp` is provided for backwards compatibility. The + ``vc`` stands for "virtual circuit." + +.. option:: +yaml, +noyaml + + When enabled, this option prints the responses (and, if :option:`+qr` is in use, also the + outgoing queries) in a detailed YAML format. + +.. option:: +zflag, +nozflag + + This option sets [or does not set] the last unassigned DNS header flag in a DNS query. + This flag is off by default. + +Multiple Queries +~~~~~~~~~~~~~~~~ + +The BIND 9 implementation of :program:`dig` supports specifying multiple +queries on the command line (in addition to supporting the :option:`-f` batch +file option). Each of those queries can be supplied with its own set of +flags, options, and query options. + +In this case, each ``query`` argument represents an individual query in +the command-line syntax described above. Each consists of any of the +standard options and flags, the name to be looked up, an optional query +type and class, and any query options that should be applied to that +query. + +A global set of query options, which should be applied to all queries, +can also be supplied. These global query options must precede the first +tuple of name, class, type, options, flags, and query options supplied +on the command line. Any global query options (except :option:`+cmd` and +:option:`+short` options) can be overridden by a query-specific set of +query options. For example: + +:: + + dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr + +shows how :program:`dig` can be used from the command line to make three +lookups: an ANY query for ``www.isc.org``, a reverse lookup of 127.0.0.1, +and a query for the NS records of ``isc.org``. A global query option of +:option:`+qr` is applied, so that :program:`dig` shows the initial query it made for +each lookup. The final query has a local query option of :option:`+noqr` which +means that :program:`dig` does not print the initial query when it looks up the +NS records for ``isc.org``. + +IDN Support +~~~~~~~~~~~ + +If :program:`dig` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. :program:`dig` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, use the parameters +:option:`+idnin` and :option:`+idnout`, or define the ``IDN_DISABLE`` environment +variable. + +Return Codes +~~~~~~~~~~~~ + +:program:`dig` return codes are: + +``0`` + DNS response received, including NXDOMAIN status + +``1`` + Usage error + +``8`` + Couldn't open batch file + +``9`` + No reply from server + +``10`` + Internal error + +Files +~~~~~ + +``/etc/resolv.conf`` + +``${HOME}/.digrc`` + +See Also +~~~~~~~~ + +:iscman:`delv(1) `, :iscman:`host(1) `, :iscman:`named(8) `, :iscman:`dnssec-keygen(8) `, :rfc:`1035`. + +Bugs +~~~~ + +There are probably too many query options. diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c new file mode 100644 index 0000000..55f7bf2 --- /dev/null +++ b/bin/dig/dighost.c @@ -0,0 +1,4989 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file + * \note + * Notice to programmers: Do not use this code as an example of how to + * use the ISC library to perform DNS lookups. Dig and Host both operate + * on the request level, since they allow fine-tuning of output and are + * intended as debugging tools. As a result, they perform many of the + * functions which could be better handled using the dns_resolver + * functions in most applications. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBIDN2 +#include +#endif /* HAVE_LIBIDN2 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +#include "dighost.h" + +#define systemlocale(l) (void)setlocale(l, "") +#define resetlocale(l) (void)setlocale(l, "C") + +dig_lookuplist_t lookup_list; +dig_serverlist_t server_list; +dig_searchlistlist_t search_list; + +static atomic_bool cancel_now = false; + +bool check_ra = false, have_ipv4 = false, have_ipv6 = false, + specified_source = false, free_now = false, usesearch = false, + showsearch = false, is_dst_up = false, keep_open = false, verbose = false, + yaml = false; +in_port_t port = 53; +bool port_set = false; +unsigned int timeout = 0; +unsigned int extrabytes; +isc_mem_t *mctx = NULL; +isc_log_t *lctx = NULL; +isc_nm_t *netmgr = NULL; +isc_taskmgr_t *taskmgr = NULL; +isc_task_t *global_task = NULL; +isc_sockaddr_t localaddr; +isc_refcount_t sendcount = 0; +isc_refcount_t recvcount = 0; +int ndots = -1; +int tries = -1; +int lookup_counter = 0; + +static char servercookie[256]; + +#ifdef HAVE_LIBIDN2 +static void +idn_locale_to_ace(const char *src, char *dst, size_t dstlen); +static void +idn_ace_to_locale(const char *src, char **dst); +static isc_result_t +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org); +#endif /* HAVE_LIBIDN2 */ + +isc_nmhandle_t *keep = NULL; +isc_sockaddr_t keepaddr; + +/*% + * Exit Codes: + * + *\li 0 Everything went well, including things like NXDOMAIN + *\li 1 Usage error + *\li 7 Got too many RR's or Names + *\li 8 Couldn't open batch file + *\li 9 No reply from server + *\li 10 Internal error + */ +int exitcode = 0; +int fatalexit = 0; +char keynametext[MXNAME]; +char keyfile[MXNAME] = ""; +char keysecret[MXNAME] = ""; +unsigned char cookie_secret[33]; +unsigned char cookie[8]; +const dns_name_t *hmacname = NULL; +unsigned int digestbits = 0; +isc_buffer_t *namebuf = NULL; +dns_tsigkey_t *tsigkey = NULL; +dst_key_t *sig0key = NULL; +bool validated = true; +bool debugging = false; +bool debugtiming = false; +bool memdebugging = false; +char *progname = NULL; +isc_mutex_t lookup_lock; +dig_lookup_t *current_lookup = NULL; + +#define DIG_MAX_ADDRESSES 20 + +/*% + * Apply and clear locks at the event level in global task. + * Can I get rid of these using shutdown events? XXX + */ +#define LOCK_LOOKUP \ + { \ + debug("lock_lookup %s:%d", __FILE__, __LINE__); \ + check_result(isc_mutex_lock((&lookup_lock)), "isc_mutex_" \ + "lock"); \ + debug("success"); \ + } +#define UNLOCK_LOOKUP \ + { \ + debug("unlock_lookup %s:%d", __FILE__, __LINE__); \ + check_result(isc_mutex_unlock((&lookup_lock)), "isc_mutex_" \ + "unlock"); \ + } + +static void +default_warnerr(const char *format, ...) { + va_list args; + + printf(";; "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); +} + +static void +default_comments(dig_lookup_t *lookup, const char *format, ...) { + va_list args; + + if (lookup->comments) { + printf(";; "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + } +} + +/* dynamic callbacks */ + +isc_result_t (*dighost_printmessage)(dig_query_t *query, + const isc_buffer_t *msgbuf, + dns_message_t *msg, bool headers); + +void (*dighost_error)(const char *format, ...) = default_warnerr; + +void (*dighost_warning)(const char *format, ...) = default_warnerr; + +void (*dighost_comments)(dig_lookup_t *lookup, const char *format, + ...) = default_comments; + +void (*dighost_received)(unsigned int bytes, isc_sockaddr_t *from, + dig_query_t *query); + +void (*dighost_trying)(char *frm, dig_lookup_t *lookup); + +void (*dighost_shutdown)(void); + +/* forward declarations */ + +#define cancel_lookup(l) _cancel_lookup(l, __FILE__, __LINE__) +static void +_cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line); + +static void +recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, + void *arg); + +static void +start_udp(dig_query_t *query); + +static void +start_tcp(dig_query_t *query); + +static void +force_next(dig_query_t *query); + +static void +launch_next_query(dig_query_t *query); + +static void +clear_current_lookup(void); + +static bool +next_origin(dig_lookup_t *oldlookup); + +static int +count_dots(char *string) { + char *s; + int i = 0; + + s = string; + while (*s != '\0') { + if (*s == '.') { + i++; + } + s++; + } + return (i); +} + +static void +hex_dump(isc_buffer_t *b) { + unsigned int len, i; + isc_region_t r; + + isc_buffer_usedregion(b, &r); + + printf("%u bytes\n", r.length); + for (len = 0; len < r.length; len++) { + printf("%02x ", r.base[len]); + if (len % 16 == 15) { + printf(" "); + for (i = len - 15; i <= len; i++) { + if (r.base[i] >= '!' && r.base[i] <= '}') { + putchar(r.base[i]); + } else { + putchar('.'); + } + } + printf("\n"); + } + } + if (len % 16 != 0) { + for (i = len; (i % 16) != 0; i++) { + printf(" "); + } + printf(" "); + for (i = ((len >> 4) << 4); i < len; i++) { + if (r.base[i] >= '!' && r.base[i] <= '}') { + putchar(r.base[i]); + } else { + putchar('.'); + } + } + printf("\n"); + } +} + +/*% + * Append 'len' bytes of 'text' at '*p', failing with + * ISC_R_NOSPACE if that would advance p past 'end'. + */ +static isc_result_t +append(const char *text, size_t len, char **p, char *end) { + if (*p + len > end) { + return (ISC_R_NOSPACE); + } + memmove(*p, text, len); + *p += len; + return (ISC_R_SUCCESS); +} + +static isc_result_t +reverse_octets(const char *in, char **p, char *end) { + const char *dot = strchr(in, '.'); + size_t len; + if (dot != NULL) { + isc_result_t result; + result = reverse_octets(dot + 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = append(".", 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + len = (int)(dot - in); + } else { + len = (int)strlen(in); + } + return (append(in, len, p, end)); +} + +isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict) { + int r; + isc_result_t result; + isc_netaddr_t addr; + + addr.family = AF_INET6; + r = inet_pton(AF_INET6, value, &addr.type.in6); + if (r > 0) { + /* This is a valid IPv6 address. */ + dns_fixedname_t fname; + dns_name_t *name; + unsigned int options = 0; + + name = dns_fixedname_initname(&fname); + result = dns_byaddr_createptrname(&addr, options, name); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_name_format(name, reverse, (unsigned int)len); + return (ISC_R_SUCCESS); + } else { + /* + * Not a valid IPv6 address. Assume IPv4. + * If 'strict' is not set, construct the + * in-addr.arpa name by blindly reversing + * octets whether or not they look like integers, + * so that this can be used for RFC2317 names + * and such. + */ + char *p = reverse; + char *end = reverse + len; + if (strict && inet_pton(AF_INET, value, &addr.type.in) != 1) { + return (DNS_R_BADDOTTEDQUAD); + } + result = reverse_octets(value, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + /* Append .in-addr.arpa. and a terminating NUL. */ + result = append(".in-addr.arpa.", 15, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + return (ISC_R_SUCCESS); + } +} + +void (*dighost_pre_exit_hook)(void) = NULL; + +#if TARGET_OS_IPHONE +void +warn(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, ";; Warning: "); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} +#else /* if TARGET_OS_IPHONE */ +void +warn(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} +#endif /* if TARGET_OS_IPHONE */ + +void +digexit(void) { + if (exitcode < 10) { + exitcode = 10; + } + if (fatalexit != 0) { + exitcode = fatalexit; + } + if (dighost_pre_exit_hook != NULL) { + dighost_pre_exit_hook(); + } + exit(exitcode); +} + +void +fatal(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + digexit(); +} + +void +debug(const char *format, ...) { + va_list args; + isc_time_t t; + + if (debugging) { + fflush(stdout); + if (debugtiming) { + TIME_NOW(&t); + fprintf(stderr, "%u.%06u: ", isc_time_seconds(&t), + isc_time_nanoseconds(&t) / 1000); + } + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +void +check_result(isc_result_t result, const char *msg) { + if (result != ISC_R_SUCCESS) { + fatal("%s: %s", msg, isc_result_totext(result)); + } +} + +/*% + * Create a server structure, which is part of the lookup structure. + * This is little more than a linked list of servers to query in hopes + * of finding the answer the user is looking for + */ +dig_server_t * +make_server(const char *servname, const char *userarg) { + dig_server_t *srv; + + REQUIRE(servname != NULL); + + debug("make_server(%s)", servname); + srv = isc_mem_allocate(mctx, sizeof(struct dig_server)); + strlcpy(srv->servername, servname, MXNAME); + strlcpy(srv->userarg, userarg, MXNAME); + ISC_LINK_INIT(srv, link); + return (srv); +} + +/*% + * Create a copy of the server list from the resolver configuration structure. + * The dest list must have already had ISC_LIST_INIT applied. + */ +static void +get_server_list(irs_resconf_t *resconf) { + isc_sockaddrlist_t *servers; + isc_sockaddr_t *sa; + dig_server_t *newsrv; + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + + sizeof("%4000000000")]; + debug("get_server_list()"); + servers = irs_resconf_getnameservers(resconf); + for (sa = ISC_LIST_HEAD(*servers); sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) + { + int pf = isc_sockaddr_pf(sa); + isc_netaddr_t na; + isc_result_t result; + isc_buffer_t b; + + if (pf == AF_INET && !have_ipv4) { + continue; + } + if (pf == AF_INET6 && !have_ipv6) { + continue; + } + + isc_buffer_init(&b, tmp, sizeof(tmp)); + isc_netaddr_fromsockaddr(&na, sa); + result = isc_netaddr_totext(&na, &b); + if (result != ISC_R_SUCCESS) { + continue; + } + isc_buffer_putuint8(&b, 0); + if (pf == AF_INET6 && na.zone != 0) { + char buf[sizeof("%4000000000")]; + snprintf(buf, sizeof(buf), "%%%u", na.zone); + strlcat(tmp, buf, sizeof(tmp)); + } + newsrv = make_server(tmp, tmp); + ISC_LINK_INIT(newsrv, link); + ISC_LIST_APPEND(server_list, newsrv, link); + } +} + +void +flush_server_list(void) { + dig_server_t *s, *ps; + + debug("flush_server_list()"); + s = ISC_LIST_HEAD(server_list); + while (s != NULL) { + ps = s; + s = ISC_LIST_NEXT(s, link); + ISC_LIST_DEQUEUE(server_list, ps, link); + isc_mem_free(mctx, ps); + } +} + +void +set_nameserver(char *opt) { + isc_result_t result; + isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES]; + isc_netaddr_t netaddr; + int count, i; + dig_server_t *srv; + char tmp[ISC_NETADDR_FORMATSIZE]; + + if (opt == NULL) { + return; + } + + result = bind9_getaddresses(opt, 0, sockaddrs, DIG_MAX_ADDRESSES, + &count); + if (result != ISC_R_SUCCESS) { + fatal("couldn't get address for '%s': %s", opt, + isc_result_totext(result)); + } + + flush_server_list(); + + for (i = 0; i < count; i++) { + isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]); + isc_netaddr_format(&netaddr, tmp, sizeof(tmp)); + srv = make_server(tmp, opt); + if (srv == NULL) { + fatal("memory allocation failure"); + } + ISC_LIST_APPEND(server_list, srv, link); + } +} + +/*% + * Produce a cloned server list. The dest list must have already had + * ISC_LIST_INIT applied. + */ +void +clone_server_list(dig_serverlist_t src, dig_serverlist_t *dest) { + dig_server_t *srv, *newsrv; + + debug("clone_server_list()"); + srv = ISC_LIST_HEAD(src); + while (srv != NULL) { + newsrv = make_server(srv->servername, srv->userarg); + ISC_LINK_INIT(newsrv, link); + ISC_LIST_ENQUEUE(*dest, newsrv, link); + srv = ISC_LIST_NEXT(srv, link); + } +} + +/*% + * Create an empty lookup structure, which holds all the information needed + * to get an answer to a user's question. This structure contains two + * linked lists: the server list (servers to query) and the query list + * (outstanding queries which have been made to the listed servers). + */ +dig_lookup_t * +make_empty_lookup(void) { + dig_lookup_t *looknew; +#ifdef HAVE_LIBIDN2 + bool idn_allowed = isatty(1) ? (getenv("IDN_DISABLE") == NULL) : false; +#endif /* HAVE_LIBIDN2 */ + + debug("make_empty_lookup()"); + + INSIST(!free_now); + + looknew = isc_mem_allocate(mctx, sizeof(*looknew)); + *looknew = (dig_lookup_t){ + .pending = true, + .rdtype = dns_rdatatype_a, + .qrdtype = dns_rdatatype_a, + .rdclass = dns_rdataclass_in, + .servfail_stops = true, + .besteffort = true, + .opcode = dns_opcode_query, + .badcookie = true, +#ifdef HAVE_LIBIDN2 + .idnin = idn_allowed, + .idnout = idn_allowed, +#endif /* HAVE_LIBIDN2 */ + .udpsize = -1, + .edns = -1, + .recurse = true, + .retries = tries, + .comments = true, + .stats = true, + .section_question = true, + .section_answer = true, + .section_authority = true, + .section_additional = true, + .ednsneg = true, + }; + + dns_fixedname_init(&looknew->fdomain); + ISC_LINK_INIT(looknew, link); + ISC_LIST_INIT(looknew->q); + ISC_LIST_INIT(looknew->my_server_list); + + isc_tlsctx_cache_create(mctx, &looknew->tls_ctx_cache); + + isc_refcount_init(&looknew->references, 1); + + looknew->magic = DIG_LOOKUP_MAGIC; + + debug("make_empty_lookup() = %p->references = %" PRIuFAST32, looknew, + isc_refcount_current(&looknew->references)); + + return (looknew); +} + +#define EDNSOPT_OPTIONS 100U + +static void +cloneopts(dig_lookup_t *looknew, dig_lookup_t *lookold) { + size_t len = sizeof(looknew->ednsopts[0]) * EDNSOPT_OPTIONS; + size_t i; + looknew->ednsopts = isc_mem_allocate(mctx, len); + for (i = 0; i < EDNSOPT_OPTIONS; i++) { + looknew->ednsopts[i].code = 0; + looknew->ednsopts[i].length = 0; + looknew->ednsopts[i].value = NULL; + } + looknew->ednsoptscnt = 0; + if (lookold == NULL || lookold->ednsopts == NULL) { + return; + } + + for (i = 0; i < lookold->ednsoptscnt; i++) { + len = lookold->ednsopts[i].length; + if (len != 0) { + INSIST(lookold->ednsopts[i].value != NULL); + looknew->ednsopts[i].value = isc_mem_allocate(mctx, + len); + memmove(looknew->ednsopts[i].value, + lookold->ednsopts[i].value, len); + } + looknew->ednsopts[i].code = lookold->ednsopts[i].code; + looknew->ednsopts[i].length = len; + } + looknew->ednsoptscnt = lookold->ednsoptscnt; +} + +/*% + * Clone a lookup, perhaps copying the server list. This does not clone + * the query list, since it will be regenerated by the setup_lookup() + * function, nor does it queue up the new lookup for processing. + * Caution: If you don't clone the servers, you MUST clone the server + * list separately from somewhere else, or construct it by hand. + */ +dig_lookup_t * +clone_lookup(dig_lookup_t *lookold, bool servers) { + dig_lookup_t *looknew; + + debug("clone_lookup()"); + + INSIST(!free_now); + + looknew = make_empty_lookup(); + strlcpy(looknew->textname, lookold->textname, MXNAME); + strlcpy(looknew->cmdline, lookold->cmdline, MXNAME); + looknew->textname[MXNAME - 1] = 0; + looknew->rdtype = lookold->rdtype; + looknew->qrdtype = lookold->qrdtype; + looknew->rdclass = lookold->rdclass; + looknew->rdtypeset = lookold->rdtypeset; + looknew->rdclassset = lookold->rdclassset; + looknew->doing_xfr = lookold->doing_xfr; + looknew->ixfr_serial = lookold->ixfr_serial; + looknew->trace = lookold->trace; + looknew->trace_root = lookold->trace_root; + looknew->identify = lookold->identify; + looknew->identify_previous_line = lookold->identify_previous_line; + looknew->ignore = lookold->ignore; + looknew->servfail_stops = lookold->servfail_stops; + looknew->besteffort = lookold->besteffort; + looknew->dns64prefix = lookold->dns64prefix; + looknew->dnssec = lookold->dnssec; + looknew->ednsflags = lookold->ednsflags; + looknew->opcode = lookold->opcode; + looknew->expire = lookold->expire; + looknew->nsid = lookold->nsid; + looknew->tcp_keepalive = lookold->tcp_keepalive; + looknew->header_only = lookold->header_only; + looknew->https_mode = lookold->https_mode; + if (lookold->https_path != NULL) { + looknew->https_path = isc_mem_strdup(mctx, lookold->https_path); + } + looknew->https_get = lookold->https_get; + looknew->http_plain = lookold->http_plain; + + looknew->tls_ca_set = lookold->tls_ca_set; + if (lookold->tls_ca_file != NULL) { + looknew->tls_ca_file = isc_mem_strdup(mctx, + lookold->tls_ca_file); + }; + + looknew->tls_hostname_set = lookold->tls_hostname_set; + if (lookold->tls_hostname != NULL) { + looknew->tls_hostname = isc_mem_strdup(mctx, + lookold->tls_hostname); + } + + looknew->tls_key_file_set = lookold->tls_key_file_set; + if (lookold->tls_key_file != NULL) { + looknew->tls_key_file = isc_mem_strdup(mctx, + lookold->tls_key_file); + } + + looknew->tls_cert_file_set = lookold->tls_cert_file_set; + if (lookold->tls_cert_file != NULL) { + looknew->tls_cert_file = isc_mem_strdup(mctx, + lookold->tls_cert_file); + } + + looknew->showbadcookie = lookold->showbadcookie; + looknew->sendcookie = lookold->sendcookie; + looknew->seenbadcookie = lookold->seenbadcookie; + looknew->badcookie = lookold->badcookie; + looknew->cookie = lookold->cookie; + if (lookold->ednsopts != NULL) { + cloneopts(looknew, lookold); + } else { + looknew->ednsopts = NULL; + looknew->ednsoptscnt = 0; + } + looknew->ednsneg = lookold->ednsneg; + looknew->padding = lookold->padding; + looknew->multiline = lookold->multiline; + looknew->nottl = lookold->nottl; + looknew->noclass = lookold->noclass; + looknew->onesoa = lookold->onesoa; + looknew->use_usec = lookold->use_usec; + looknew->nocrypto = lookold->nocrypto; + looknew->ttlunits = lookold->ttlunits; + looknew->expandaaaa = lookold->expandaaaa; + looknew->qr = lookold->qr; + looknew->idnin = lookold->idnin; + looknew->idnout = lookold->idnout; + looknew->udpsize = lookold->udpsize; + looknew->edns = lookold->edns; + looknew->recurse = lookold->recurse; + looknew->aaonly = lookold->aaonly; + looknew->adflag = lookold->adflag; + looknew->cdflag = lookold->cdflag; + looknew->raflag = lookold->raflag; + looknew->tcflag = lookold->tcflag; + looknew->print_unknown_format = lookold->print_unknown_format; + looknew->zflag = lookold->zflag; + looknew->setqid = lookold->setqid; + looknew->qid = lookold->qid; + looknew->ns_search_only = lookold->ns_search_only; + looknew->tcp_mode = lookold->tcp_mode; + looknew->tcp_mode_set = lookold->tcp_mode_set; + looknew->tls_mode = lookold->tls_mode; + looknew->comments = lookold->comments; + looknew->stats = lookold->stats; + looknew->section_question = lookold->section_question; + looknew->section_answer = lookold->section_answer; + looknew->section_authority = lookold->section_authority; + looknew->section_additional = lookold->section_additional; + looknew->origin = lookold->origin; + looknew->retries = lookold->retries; + looknew->tsigctx = NULL; + looknew->need_search = lookold->need_search; + looknew->done_as_is = lookold->done_as_is; + looknew->rrcomments = lookold->rrcomments; + looknew->fuzzing = lookold->fuzzing; + looknew->fuzztime = lookold->fuzztime; + + if (lookold->ecs_addr != NULL) { + size_t len = sizeof(isc_sockaddr_t); + looknew->ecs_addr = isc_mem_allocate(mctx, len); + memmove(looknew->ecs_addr, lookold->ecs_addr, len); + } + + dns_name_copy(dns_fixedname_name(&lookold->fdomain), + dns_fixedname_name(&looknew->fdomain)); + + if (servers) { + if (lookold->tls_ctx_cache != NULL) { + isc_tlsctx_cache_detach(&looknew->tls_ctx_cache); + isc_tlsctx_cache_attach(lookold->tls_ctx_cache, + &looknew->tls_ctx_cache); + } + clone_server_list(lookold->my_server_list, + &looknew->my_server_list); + } + + isc_refcount_init(&looknew->references, 1); + + looknew->magic = DIG_LOOKUP_MAGIC; + + return (looknew); +} + +/*% + * Requeue a lookup for further processing, perhaps copying the server + * list. The new lookup structure is returned to the caller, and is + * queued for processing. If servers are not cloned in the requeue, they + * must be added before allowing the current event to complete, since the + * completion of the event may result in the next entry on the lookup + * queue getting run. + */ +dig_lookup_t * +requeue_lookup(dig_lookup_t *lookold, bool servers) { + dig_lookup_t *looknew = NULL; + + debug("requeue_lookup()"); + + lookup_counter++; + if (lookup_counter > LOOKUP_LIMIT) { + fatal("too many lookups"); + } + + looknew = clone_lookup(lookold, servers); + INSIST(looknew != NULL); + + debug("before insertion, init@%p -> %p, new@%p -> %p", lookold, + lookold->link.next, looknew, looknew->link.next); + ISC_LIST_PREPEND(lookup_list, looknew, link); + debug("after insertion, init -> %p, new = %p, new -> %p", lookold, + looknew, looknew->link.next); + return (looknew); +} + +void +setup_text_key(void) { + isc_result_t result; + dns_name_t keyname; + isc_buffer_t secretbuf; + unsigned int secretsize; + unsigned char *secretstore; + + debug("setup_text_key()"); + isc_buffer_allocate(mctx, &namebuf, MXNAME); + dns_name_init(&keyname, NULL); + isc_buffer_putstr(namebuf, keynametext); + secretsize = (unsigned int)strlen(keysecret) * 3 / 4; + secretstore = isc_mem_allocate(mctx, secretsize); + isc_buffer_init(&secretbuf, secretstore, secretsize); + result = isc_base64_decodestring(keysecret, &secretbuf); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + secretsize = isc_buffer_usedlength(&secretbuf); + + if (hmacname == NULL) { + result = DST_R_UNSUPPORTEDALG; + goto failure; + } + + result = dns_name_fromtext(&keyname, namebuf, dns_rootname, 0, namebuf); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + result = dns_tsigkey_create(&keyname, hmacname, secretstore, + (int)secretsize, false, NULL, 0, 0, mctx, + NULL, &tsigkey); +failure: + if (result != ISC_R_SUCCESS) { + printf(";; Couldn't create key %s: %s\n", keynametext, + isc_result_totext(result)); + } else { + dst_key_setbits(tsigkey->key, digestbits); + } + + isc_mem_free(mctx, secretstore); + dns_name_invalidate(&keyname); + isc_buffer_free(&namebuf); +} + +static isc_result_t +parse_uint_helper(uint32_t *uip, const char *value, uint32_t max, + const char *desc, int base) { + uint32_t n; + isc_result_t result = isc_parse_uint32(&n, value, base); + if (result == ISC_R_SUCCESS && n > max) { + result = ISC_R_RANGE; + } + if (result != ISC_R_SUCCESS) { + printf("invalid %s '%s': %s\n", desc, value, + isc_result_totext(result)); + return (result); + } + *uip = n; + return (ISC_R_SUCCESS); +} + +isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { + return (parse_uint_helper(uip, value, max, desc, 10)); +} + +isc_result_t +parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { + return (parse_uint_helper(uip, value, max, desc, 0)); +} + +static uint32_t +parse_bits(char *arg, const char *desc, uint32_t max) { + isc_result_t result; + uint32_t tmp; + + result = parse_uint(&tmp, arg, max, desc); + if (result != ISC_R_SUCCESS) { + fatal("couldn't parse digest bits"); + } + tmp = (tmp + 7) & ~0x7U; + return (tmp); +} + +isc_result_t +parse_netprefix(isc_sockaddr_t **sap, const char *value) { + isc_result_t result = ISC_R_SUCCESS; + isc_sockaddr_t *sa = NULL; + struct in_addr in4; + struct in6_addr in6; + uint32_t prefix_length = 0xffffffff; + char *slash = NULL; + bool parsed = false; + bool prefix_parsed = false; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")]; + + REQUIRE(sap != NULL && *sap == NULL); + + if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) { + fatal("invalid prefix '%s'\n", value); + } + + sa = isc_mem_allocate(mctx, sizeof(*sa)); + memset(sa, 0, sizeof(*sa)); + + if (strcmp(buf, "0") == 0) { + sa->type.sa.sa_family = AF_UNSPEC; + prefix_length = 0; + goto done; + } + + slash = strchr(buf, '/'); + if (slash != NULL) { + *slash = '\0'; + result = isc_parse_uint32(&prefix_length, slash + 1, 10); + if (result != ISC_R_SUCCESS) { + fatal("invalid prefix length in '%s': %s\n", value, + isc_result_totext(result)); + } + prefix_parsed = true; + } + + if (inet_pton(AF_INET6, buf, &in6) == 1) { + parsed = true; + isc_sockaddr_fromin6(sa, &in6, 0); + if (prefix_length > 128) { + prefix_length = 128; + } + } else if (inet_pton(AF_INET, buf, &in4) == 1) { + parsed = true; + isc_sockaddr_fromin(sa, &in4, 0); + if (prefix_length > 32) { + prefix_length = 32; + } + } else if (prefix_parsed) { + int i; + + for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) { + strlcat(buf, ".0", sizeof(buf)); + if (inet_pton(AF_INET, buf, &in4) == 1) { + parsed = true; + isc_sockaddr_fromin(sa, &in4, 0); + break; + } + } + + if (prefix_length > 32) { + prefix_length = 32; + } + } + + if (!parsed) { + fatal("invalid address '%s'", value); + } + +done: + sa->length = prefix_length; + *sap = sa; + + return (ISC_R_SUCCESS); +} + +/* + * Parse HMAC algorithm specification + */ +void +parse_hmac(const char *hmac) { + char buf[20]; + size_t len; + + REQUIRE(hmac != NULL); + + len = strlen(hmac); + if (len >= sizeof(buf)) { + fatal("unknown key type '%.*s'", (int)len, hmac); + } + strlcpy(buf, hmac, sizeof(buf)); + + digestbits = 0; + + if (strcasecmp(buf, "hmac-md5") == 0) { + hmacname = DNS_TSIG_HMACMD5_NAME; + } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { + hmacname = DNS_TSIG_HMACMD5_NAME; + digestbits = parse_bits(&buf[9], "digest-bits [0..128]", 128); + } else if (strcasecmp(buf, "hmac-sha1") == 0) { + hmacname = DNS_TSIG_HMACSHA1_NAME; + digestbits = 0; + } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { + hmacname = DNS_TSIG_HMACSHA1_NAME; + digestbits = parse_bits(&buf[10], "digest-bits [0..160]", 160); + } else if (strcasecmp(buf, "hmac-sha224") == 0) { + hmacname = DNS_TSIG_HMACSHA224_NAME; + } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA224_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..224]", 224); + } else if (strcasecmp(buf, "hmac-sha256") == 0) { + hmacname = DNS_TSIG_HMACSHA256_NAME; + } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA256_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..256]", 256); + } else if (strcasecmp(buf, "hmac-sha384") == 0) { + hmacname = DNS_TSIG_HMACSHA384_NAME; + } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA384_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..384]", 384); + } else if (strcasecmp(buf, "hmac-sha512") == 0) { + hmacname = DNS_TSIG_HMACSHA512_NAME; + } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA512_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..512]", 512); + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid TSIG algorithm %s\n", + buf); + } +} + +/* + * Get a key from a named.conf format keyfile + */ +static isc_result_t +read_confkey(void) { + cfg_parser_t *pctx = NULL; + cfg_obj_t *file = NULL; + const cfg_obj_t *keyobj = NULL; + const cfg_obj_t *secretobj = NULL; + const cfg_obj_t *algorithmobj = NULL; + const char *keyname; + const char *secretstr; + const char *algorithm; + isc_result_t result; + + if (!isc_file_exists(keyfile)) { + return (ISC_R_FILENOTFOUND); + } + + result = cfg_parser_create(mctx, NULL, &pctx); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey, &file); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_map_get(file, "key", &keyobj); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + (void)cfg_map_get(keyobj, "secret", &secretobj); + (void)cfg_map_get(keyobj, "algorithm", &algorithmobj); + if (secretobj == NULL || algorithmobj == NULL) { + fatal("key must have algorithm and secret"); + } + + keyname = cfg_obj_asstring(cfg_map_getname(keyobj)); + secretstr = cfg_obj_asstring(secretobj); + algorithm = cfg_obj_asstring(algorithmobj); + + strlcpy(keynametext, keyname, sizeof(keynametext)); + strlcpy(keysecret, secretstr, sizeof(keysecret)); + parse_hmac(algorithm); + setup_text_key(); + +cleanup: + if (pctx != NULL) { + if (file != NULL) { + cfg_obj_destroy(pctx, &file); + } + cfg_parser_destroy(&pctx); + } + + return (result); +} + +void +setup_file_key(void) { + isc_result_t result; + dst_key_t *dstkey = NULL; + + debug("setup_file_key()"); + + if (sig0key != NULL) { + dst_key_free(&sig0key); + } + + /* Try reading the key from a K* pair */ + result = dst_key_fromnamedfile( + keyfile, NULL, DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx, &dstkey); + + /* If that didn't work, try reading it as a session.key keyfile */ + if (result != ISC_R_SUCCESS) { + result = read_confkey(); + if (result == ISC_R_SUCCESS) { + return; + } + } + + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "Couldn't read key from %s: %s\n", keyfile, + isc_result_totext(result)); + goto failure; + } + + switch (dst_key_alg(dstkey)) { + case DST_ALG_HMACMD5: + hmacname = DNS_TSIG_HMACMD5_NAME; + break; + case DST_ALG_HMACSHA1: + hmacname = DNS_TSIG_HMACSHA1_NAME; + break; + case DST_ALG_HMACSHA224: + hmacname = DNS_TSIG_HMACSHA224_NAME; + break; + case DST_ALG_HMACSHA256: + hmacname = DNS_TSIG_HMACSHA256_NAME; + break; + case DST_ALG_HMACSHA384: + hmacname = DNS_TSIG_HMACSHA384_NAME; + break; + case DST_ALG_HMACSHA512: + hmacname = DNS_TSIG_HMACSHA512_NAME; + break; + } + + if (hmacname != NULL) { + result = dns_tsigkey_createfromkey( + dst_key_name(dstkey), hmacname, dstkey, false, NULL, 0, + 0, mctx, NULL, &tsigkey); + if (result != ISC_R_SUCCESS) { + printf(";; Couldn't create key %s: %s\n", keynametext, + isc_result_totext(result)); + goto failure; + } + } else { + dst_key_attach(dstkey, &sig0key); + dst_key_free(&dstkey); + } +failure: + if (dstkey != NULL) { + dst_key_free(&dstkey); + } +} + +static dig_searchlist_t * +make_searchlist_entry(char *domain) { + dig_searchlist_t *search; + search = isc_mem_allocate(mctx, sizeof(*search)); + strlcpy(search->origin, domain, MXNAME); + search->origin[MXNAME - 1] = 0; + ISC_LINK_INIT(search, link); + return (search); +} + +static void +clear_searchlist(void) { + dig_searchlist_t *search; + while ((search = ISC_LIST_HEAD(search_list)) != NULL) { + ISC_LIST_UNLINK(search_list, search, link); + isc_mem_free(mctx, search); + } +} + +static void +create_search_list(irs_resconf_t *resconf) { + irs_resconf_searchlist_t *list; + irs_resconf_search_t *entry; + dig_searchlist_t *search; + + debug("create_search_list()"); + clear_searchlist(); + + list = irs_resconf_getsearchlist(resconf); + for (entry = ISC_LIST_HEAD(*list); entry != NULL; + entry = ISC_LIST_NEXT(entry, link)) + { + search = make_searchlist_entry(entry->domain); + ISC_LIST_APPEND(search_list, search, link); + } +} + +/*% + * Append 'addr' to the list of servers to be queried. This function is only + * called when no server addresses are explicitly specified and either libirs + * returns an empty list of servers to use or none of the addresses returned by + * libirs are usable due to the specified address family restrictions. + */ +static void +add_fallback_nameserver(const char *addr) { + dig_server_t *server = make_server(addr, addr); + ISC_LINK_INIT(server, link); + ISC_LIST_APPEND(server_list, server, link); +} + +/*% + * Setup the system as a whole, reading key information and resolv.conf + * settings. + */ +void +setup_system(bool ipv4only, bool ipv6only) { + irs_resconf_t *resconf = NULL; + isc_result_t result; + + debug("setup_system()"); + + if (ipv4only) { + if (have_ipv4) { + isc_net_disableipv6(); + have_ipv6 = false; + } else { + fatal("can't find IPv4 networking"); + } + } + + if (ipv6only) { + if (have_ipv6) { + isc_net_disableipv4(); + have_ipv4 = false; + } else { + fatal("can't find IPv6 networking"); + } + } + + result = irs_resconf_load(mctx, RESOLV_CONF, &resconf); + if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { + fatal("parse of %s failed", RESOLV_CONF); + } + + create_search_list(resconf); + if (ndots == -1) { + ndots = irs_resconf_getndots(resconf); + debug("ndots is %d.", ndots); + } + if (timeout == 0) { + timeout = irs_resconf_gettimeout(resconf); + debug("timeout is %d.", timeout); + } + if (tries == -1) { + tries = irs_resconf_getattempts(resconf); + if (tries == 0) { + tries = 3; + } + debug("retries is %d.", tries); + } + + /* If user doesn't specify server use nameservers from resolv.conf. */ + if (ISC_LIST_EMPTY(server_list)) { + get_server_list(resconf); + } + + /* If we don't find a nameserver fall back to localhost */ + if (ISC_LIST_EMPTY(server_list)) { + if (have_ipv6) { + add_fallback_nameserver("::1"); + } + if (have_ipv4) { + add_fallback_nameserver("127.0.0.1"); + } + } + + irs_resconf_destroy(&resconf); + + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + + isc_nonce_buf(cookie_secret, sizeof(cookie_secret)); +} + +/*% + * Override the search list derived from resolv.conf by 'domain'. + */ +void +set_search_domain(char *domain) { + dig_searchlist_t *search; + + clear_searchlist(); + search = make_searchlist_entry(domain); + ISC_LIST_APPEND(search_list, search, link); +} + +/*% + * Setup the ISC and DNS libraries for use by the system. + */ +void +setup_libs(void) { + isc_result_t result; + isc_logconfig_t *logconfig = NULL; + + debug("setup_libs()"); + + result = isc_net_probeipv4(); + if (result == ISC_R_SUCCESS) { + have_ipv4 = true; + } + + result = isc_net_probeipv6(); + if (result == ISC_R_SUCCESS) { + have_ipv6 = true; + } + if (!have_ipv6 && !have_ipv4) { + fatal("can't find either v4 or v6 networking"); + } + + isc_mem_create(&mctx); + isc_mem_setname(mctx, "dig"); + + isc_log_create(mctx, &lctx, &logconfig); + isc_log_setcontext(lctx); + dns_log_init(lctx); + dns_log_setcontext(lctx); + + result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL); + check_result(result, "isc_log_usechannel"); + + isc_log_setdebuglevel(lctx, 0); + + isc_managers_create(mctx, 1, 0, &netmgr, &taskmgr, NULL); + + result = isc_task_create(taskmgr, 0, &global_task); + check_result(result, "isc_task_create"); + isc_task_setname(global_task, "dig", NULL); + + result = dst_lib_init(mctx, NULL); + check_result(result, "dst_lib_init"); + is_dst_up = true; + + isc_mutex_init(&lookup_lock); +} + +typedef struct dig_ednsoptname { + uint32_t code; + const char *name; +} dig_ednsoptname_t; + +dig_ednsoptname_t optnames[] = { + { 1, "LLQ" }, /* draft-sekar-dns-llq */ + { 3, "NSID" }, /* RFC 5001 */ + { 5, "DAU" }, /* RFC 6975 */ + { 6, "DHU" }, /* RFC 6975 */ + { 7, "N3U" }, /* RFC 6975 */ + { 8, "ECS" }, /* RFC 7871 */ + { 9, "EXPIRE" }, /* RFC 7314 */ + { 10, "COOKIE" }, /* RFC 7873 */ + { 11, "KEEPALIVE" }, /* RFC 7828 */ + { 12, "PADDING" }, /* RFC 7830 */ + { 12, "PAD" }, /* shorthand */ + { 13, "CHAIN" }, /* RFC 7901 */ + { 14, "KEY-TAG" }, /* RFC 8145 */ + { 15, "EDE" }, /* ietf-dnsop-extended-error-16 */ + { 16, "CLIENT-TAG" }, /* draft-bellis-dnsop-edns-tags */ + { 17, "SERVER-TAG" }, /* draft-bellis-dnsop-edns-tags */ + { 26946, "DEVICEID" }, /* Brian Hartvigsen */ +}; + +#define N_EDNS_OPTNAMES (sizeof(optnames) / sizeof(optnames[0])) + +void +save_opt(dig_lookup_t *lookup, char *code, char *value) { + isc_result_t result; + uint32_t num = 0; + isc_buffer_t b; + bool found = false; + unsigned int i; + + if (lookup->ednsoptscnt >= EDNSOPT_OPTIONS) { + fatal("too many ednsopts"); + } + + for (i = 0; i < N_EDNS_OPTNAMES; i++) { + if (strcasecmp(code, optnames[i].name) == 0) { + num = optnames[i].code; + found = true; + break; + } + } + + if (!found) { + result = parse_uint(&num, code, 65535, "ednsopt"); + if (result != ISC_R_SUCCESS) { + fatal("bad edns code point: %s", code); + } + } + + if (lookup->ednsopts == NULL) { + cloneopts(lookup, NULL); + } + INSIST(lookup->ednsopts != NULL); + + if (lookup->ednsopts[lookup->ednsoptscnt].value != NULL) { + isc_mem_free(mctx, lookup->ednsopts[lookup->ednsoptscnt].value); + } + + lookup->ednsopts[lookup->ednsoptscnt].code = num; + lookup->ednsopts[lookup->ednsoptscnt].length = 0; + lookup->ednsopts[lookup->ednsoptscnt].value = NULL; + + if (value != NULL) { + char *buf; + buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1); + isc_buffer_init(&b, buf, (unsigned int)strlen(value) / 2 + 1); + result = isc_hex_decodestring(value, &b); + check_result(result, "isc_hex_decodestring"); + lookup->ednsopts[lookup->ednsoptscnt].value = + isc_buffer_base(&b); + lookup->ednsopts[lookup->ednsoptscnt].length = + isc_buffer_usedlength(&b); + } + + lookup->ednsoptscnt++; +} + +/*% + * Add EDNS0 option record to a message. Currently, the only supported + * options are UDP buffer size, the DO bit, and EDNS options + * (e.g., NSID, COOKIE, client-subnet) + */ +static void +add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, + dns_ednsopt_t *opts, size_t count) { + dns_rdataset_t *rdataset = NULL; + isc_result_t result; + + debug("add_opt()"); + result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, + opts, count); + check_result(result, "dns_message_buildopt"); + result = dns_message_setopt(msg, rdataset); + check_result(result, "dns_message_setopt"); +} + +/*% + * Add a question section to a message, asking for the specified name, + * type, and class. + */ +static void +add_question(dns_message_t *message, dns_name_t *name, dns_rdataclass_t rdclass, + dns_rdatatype_t rdtype) { + dns_rdataset_t *rdataset; + isc_result_t result; + + debug("add_question()"); + rdataset = NULL; + result = dns_message_gettemprdataset(message, &rdataset); + check_result(result, "dns_message_gettemprdataset()"); + dns_rdataset_makequestion(rdataset, rdclass, rdtype); + ISC_LIST_APPEND(name->list, rdataset, link); +} + +/*% + * Check if we're done with all the queued lookups, which is true iff + * all sockets, sends, and recvs are accounted for (counters == 0), + * and the lookup list is empty. + * If we are done, pass control back out to dighost_shutdown() (which is + * part of dig.c, host.c, or nslookup.c) to either shutdown the system as + * a whole or reseed the lookup list. + */ +static void +check_if_done(void) { + dig_lookup_t *lookup = NULL; + + debug("check_if_done()"); + debug("list %s", ISC_LIST_EMPTY(lookup_list) ? "empty" : "full"); + + lookup = ISC_LIST_HEAD(lookup_list); + while (lookup != NULL) { + dig_lookup_t *next = NULL; + debug("pending lookup %p", lookup); + next = ISC_LIST_NEXT(lookup, link); + lookup = next; + } + + if (ISC_LIST_EMPTY(lookup_list) && current_lookup == NULL && + isc_refcount_current(&sendcount) == 0) + { + INSIST(isc_refcount_current(&recvcount) == 0); + debug("shutting down"); + dighost_shutdown(); + } +} + +/*% + * Check if we're done with all the queries in the lookup, except for + * the `except_q` query (can be NULL if no exception is required). + * Expects `l` to be a valid and locked lookup. + */ +static bool +check_if_queries_done(dig_lookup_t *l, dig_query_t *except_q) { + dig_query_t *q = ISC_LIST_HEAD(l->q); + + debug("check_if_queries_done(%p)", l); + + while (q != NULL) { + if (!q->started || isc_refcount_current(&q->references) > 1) { + if (!q->canceled && q != except_q) { + debug("there is a pending query %p", q); + return (false); + } + } + q = ISC_LIST_NEXT(q, link); + } + + return (true); +} + +static void +_destroy_lookup(dig_lookup_t *lookup) { + dig_server_t *s; + void *ptr; + + REQUIRE(lookup != NULL); + REQUIRE(ISC_LIST_EMPTY(lookup->q)); + + debug("destroy_lookup"); + + isc_refcount_destroy(&lookup->references); + + s = ISC_LIST_HEAD(lookup->my_server_list); + while (s != NULL) { + debug("freeing server %p belonging to %p", s, lookup); + ptr = s; + s = ISC_LIST_NEXT(s, link); + ISC_LIST_DEQUEUE(lookup->my_server_list, (dig_server_t *)ptr, + link); + isc_mem_free(mctx, ptr); + } + if (lookup->sendmsg != NULL) { + dns_message_detach(&lookup->sendmsg); + } + if (lookup->querysig != NULL) { + debug("freeing buffer %p", lookup->querysig); + isc_buffer_free(&lookup->querysig); + } + if (lookup->sendspace != NULL) { + isc_mem_put(mctx, lookup->sendspace, COMMSIZE); + } + + if (lookup->tsigctx != NULL) { + dst_context_destroy(&lookup->tsigctx); + } + + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + } + + if (lookup->ednsopts != NULL) { + size_t i; + for (i = 0; i < EDNSOPT_OPTIONS; i++) { + if (lookup->ednsopts[i].value != NULL) { + isc_mem_free(mctx, lookup->ednsopts[i].value); + } + } + isc_mem_free(mctx, lookup->ednsopts); + } + + if (lookup->https_path) { + isc_mem_free(mctx, lookup->https_path); + } + + if (lookup->tls_ctx_cache != NULL) { + isc_tlsctx_cache_detach(&lookup->tls_ctx_cache); + } + + if (lookup->tls_ca_file != NULL) { + isc_mem_free(mctx, lookup->tls_ca_file); + } + + if (lookup->tls_hostname != NULL) { + isc_mem_free(mctx, lookup->tls_hostname); + } + + if (lookup->tls_key_file != NULL) { + isc_mem_free(mctx, lookup->tls_key_file); + } + + if (lookup->tls_cert_file != NULL) { + isc_mem_free(mctx, lookup->tls_cert_file); + } + + isc_mem_free(mctx, lookup); +} + +#define lookup_attach(s, t) _lookup_attach(s, t, __FILE__, __LINE__) +static void +_lookup_attach(dig_lookup_t *lookup, dig_lookup_t **lookupp, const char *file, + unsigned int line) { + REQUIRE(DIG_VALID_LOOKUP(lookup)); + REQUIRE(lookupp != NULL && *lookupp == NULL); + + debug("%s:%u:lookup_attach(%p) = %" PRIuFAST32, file, line, lookup, + isc_refcount_current(&lookup->references) + 1); + + (void)isc_refcount_increment(&lookup->references); + + *lookupp = lookup; +} + +#define lookup_detach(l) _lookup_detach(l, __FILE__, __LINE__) +static void +_lookup_detach(dig_lookup_t **lookupp, const char *file, unsigned int line) { + REQUIRE(DIG_VALID_LOOKUP(*lookupp)); + + dig_lookup_t *lookup = *lookupp; + *lookupp = NULL; + + debug("%s:%u:lookup_detach(%p) = %" PRIuFAST32, file, line, lookup, + isc_refcount_current(&lookup->references) - 1); + + if (isc_refcount_decrement(&lookup->references) == 1) { + _destroy_lookup(lookup); + if (lookup == current_lookup) { + current_lookup = NULL; + start_lookup(); + } + } +} + +void +destroy_lookup(dig_lookup_t *lookup) { + REQUIRE(DIG_VALID_LOOKUP(lookup)); + + REQUIRE(isc_refcount_decrement(&lookup->references) == 1); + _destroy_lookup(lookup); +} + +/*% + * Destroy a query when we're done with it. WARNING: This routine + * WILL invalidate the query pointer. + */ +static void +destroy_query(dig_query_t *query, const char *file, unsigned int line) { + debug("%s:%u:destroy_query(%p) = %" PRIuFAST32, file, line, query, + isc_refcount_current(&query->references)); + + isc_refcount_destroy(&query->references); + + lookup_detach(&query->lookup); + + INSIST(query->recvspace != NULL); + + isc_mem_put(mctx, query->recvspace, COMMSIZE); + isc_mem_put(mctx, query->tmpsendspace, COMMSIZE); + + query->magic = 0; + isc_mem_free(mctx, query); +} + +#define query_attach(s, t) _query_attach(s, t, __FILE__, __LINE__) + +static void +_query_attach(dig_query_t *source, dig_query_t **targetp, const char *file, + unsigned int line) { + REQUIRE(DIG_VALID_QUERY(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + debug("%s:%u:query_attach(%p) = %" PRIuFAST32, file, line, source, + isc_refcount_current(&source->references) + 1); + + (void)isc_refcount_increment(&source->references); + + *targetp = source; +} + +#define query_detach(q) _query_detach(q, __FILE__, __LINE__) + +static void +_query_detach(dig_query_t **queryp, const char *file, unsigned int line) { + dig_query_t *query = NULL; + dig_lookup_t *lookup = NULL; + + REQUIRE(DIG_VALID_QUERY(*queryp)); + + query = *queryp; + *queryp = NULL; + + lookup = query->lookup; + + if (lookup->current_query == query) { + query_detach(&lookup->current_query); + } + + debug("%s:%u:query_detach(%p) = %" PRIuFAST32, file, line, query, + isc_refcount_current(&query->references) - 1); + + if (isc_refcount_decrement(&query->references) == 1) { + INSIST(query->readhandle == NULL); + INSIST(query->sendhandle == NULL); + + if (ISC_LINK_LINKED(query, link)) { + ISC_LIST_UNLINK(lookup->q, query, link); + } + destroy_query(query, file, line); + } +} + +/*% + * If we can, start the next lookup in the queue running. + * This assumes that the lookup on the head of the queue hasn't been + * started yet. It also removes the lookup from the head of the queue, + * setting the current_lookup pointer pointing to it. + */ +void +start_lookup(void) { + debug("start_lookup()"); + + if (atomic_load(&cancel_now)) { + return; + } + + /* + * If there's a current lookup running, we really shouldn't get + * here. + */ + INSIST(current_lookup == NULL); + + current_lookup = ISC_LIST_HEAD(lookup_list); + + /* + * Put the current lookup somewhere so cancel_all can find it + */ + if (current_lookup != NULL) { + /* + * Formally, we should attach the lookup to the current_lookup + * and detach it from the lookup_list, but it would be one + * attach and one detach. + */ + ISC_LIST_DEQUEUE(lookup_list, current_lookup, link); + if (setup_lookup(current_lookup)) { + do_lookup(current_lookup); + } else if (next_origin(current_lookup)) { + lookup_detach(¤t_lookup); + start_lookup(); + } + } else { + check_if_done(); + } +} + +/*% + * If we can, clear the current lookup and start the next one running. + * (Note that while the reference count of current_lookup may be + * decremented, current_lookup will not be set to NULL.) + */ +static void +clear_current_lookup(void) { + dig_lookup_t *lookup = current_lookup; + + INSIST(!free_now); + + debug("clear_current_lookup()"); + + if (lookup == NULL) { + debug("current_lookup is already detached"); + return; + } + + if (lookup->cleared) { + debug("current_lookup is already cleared"); + return; + } + + if (ISC_LIST_HEAD(lookup->q) != NULL) { + debug("still have a worker"); + return; + } + + lookup->cleared = true; + debug("lookup cleared"); + + lookup_detach(&lookup); +} + +/*% + * Create and queue a new lookup as a followup to the current lookup, + * based on the supplied message and section. This is used in trace and + * name server search modes to start a new lookup using servers from + * NS records in a reply. Returns the number of followup lookups made. + */ +static int +followup_lookup(dns_message_t *msg, dig_query_t *query, dns_section_t section) { + dig_lookup_t *lookup = NULL; + dig_server_t *srv = NULL; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_name_t *name = NULL; + isc_result_t result; + bool success = false; + int numLookups = 0; + int num; + isc_result_t lresult, addresses_result; + char bad_namestr[DNS_NAME_FORMATSIZE]; + dns_name_t *domain; + bool horizontal = false, bad = false; + + INSIST(!free_now); + + debug("following up %s", query->lookup->textname); + + addresses_result = ISC_R_SUCCESS; + bad_namestr[0] = '\0'; + for (result = dns_message_firstname(msg, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(msg, section)) + { + name = NULL; + dns_message_currentname(msg, section, &name); + + if (section == DNS_SECTION_AUTHORITY) { + rdataset = NULL; + result = dns_message_findtype(name, dns_rdatatype_soa, + 0, &rdataset); + if (result == ISC_R_SUCCESS) { + return (0); + } + } + rdataset = NULL; + result = dns_message_findtype(name, dns_rdatatype_ns, 0, + &rdataset); + if (result != ISC_R_SUCCESS) { + continue; + } + + debug("found NS set"); + + if (query->lookup->trace && !query->lookup->trace_root) { + dns_namereln_t namereln; + unsigned int nlabels; + int order; + + domain = dns_fixedname_name(&query->lookup->fdomain); + namereln = dns_name_fullcompare(name, domain, &order, + &nlabels); + if (namereln == dns_namereln_equal) { + if (!horizontal) { + dighost_warning("BAD (HORIZONTAL) " + "REFERRAL"); + } + horizontal = true; + } else if (namereln != dns_namereln_subdomain) { + if (!bad) { + dighost_warning("BAD REFERRAL"); + } + bad = true; + continue; + } + } + + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + char namestr[DNS_NAME_FORMATSIZE]; + dns_rdata_ns_t ns; + + if (query->lookup->trace_root && + query->lookup->nsfound >= MXSERV) + { + break; + } + + dns_rdataset_current(rdataset, &rdata); + + query->lookup->nsfound++; + result = dns_rdata_tostruct(&rdata, &ns, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_format(&ns.name, namestr, sizeof(namestr)); + dns_rdata_freestruct(&ns); + + /* Initialize lookup if we've not yet */ + debug("found NS %s", namestr); + if (!success) { + success = true; + lookup_counter++; + lookup = requeue_lookup(query->lookup, false); + cancel_lookup(query->lookup); + lookup->doing_xfr = false; + if (!lookup->trace_root && + section == DNS_SECTION_ANSWER) + { + lookup->trace = false; + } else { + lookup->trace = query->lookup->trace; + } + lookup->ns_search_only = + query->lookup->ns_search_only; + lookup->trace_root = false; + if (lookup->ns_search_only) { + lookup->recurse = false; + } + domain = dns_fixedname_name(&lookup->fdomain); + dns_name_copy(name, domain); + } + debug("adding server %s", namestr); + num = getaddresses(lookup, namestr, &lresult); + if (lresult != ISC_R_SUCCESS) { + printf("couldn't get address for '%s': %s\n", + namestr, isc_result_totext(lresult)); + if (addresses_result == ISC_R_SUCCESS) { + addresses_result = lresult; + strlcpy(bad_namestr, namestr, + sizeof(bad_namestr)); + } + } + numLookups += num; + dns_rdata_reset(&rdata); + } + } + if (numLookups == 0 && addresses_result != ISC_R_SUCCESS) { + fatal("couldn't get address for '%s': %s", bad_namestr, + isc_result_totext(result)); + } + + if (lookup == NULL && section == DNS_SECTION_ANSWER && + (query->lookup->trace || query->lookup->ns_search_only)) + { + return (followup_lookup(msg, query, DNS_SECTION_AUTHORITY)); + } + + /* + * Randomize the order the nameserver will be tried. + */ + if (numLookups > 1) { + uint32_t i, j; + dig_serverlist_t my_server_list; + dig_server_t *next; + + ISC_LIST_INIT(my_server_list); + + i = numLookups; + for (srv = ISC_LIST_HEAD(lookup->my_server_list); srv != NULL; + srv = ISC_LIST_HEAD(lookup->my_server_list)) + { + INSIST(i > 0); + j = isc_random_uniform(i); + next = ISC_LIST_NEXT(srv, link); + while (j-- > 0 && next != NULL) { + srv = next; + next = ISC_LIST_NEXT(srv, link); + } + ISC_LIST_DEQUEUE(lookup->my_server_list, srv, link); + ISC_LIST_APPEND(my_server_list, srv, link); + i--; + } + ISC_LIST_APPENDLIST(lookup->my_server_list, my_server_list, + link); + } + + return (numLookups); +} + +/*% + * Create and queue a new lookup using the next origin from the search + * list, read in setup_system(). + * + * Return true iff there was another searchlist entry. + */ +static bool +next_origin(dig_lookup_t *oldlookup) { + dig_lookup_t *newlookup; + dig_searchlist_t *search; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + + INSIST(!free_now); + + debug("next_origin()"); + debug("following up %s", oldlookup->textname); + + if (!usesearch) { + /* + * We're not using a search list, so don't even think + * about finding the next entry. + */ + return (false); + } + + /* + * Check for a absolute name or ndots being met. + */ + name = dns_fixedname_initname(&fixed); + result = dns_name_fromstring2(name, oldlookup->textname, NULL, 0, NULL); + if (result == ISC_R_SUCCESS && + (dns_name_isabsolute(name) || + (int)dns_name_countlabels(name) > ndots)) + { + return (false); + } + + if (oldlookup->origin == NULL && !oldlookup->need_search) { + /* + * Then we just did rootorg; there's nothing left. + */ + return (false); + } + if (oldlookup->origin == NULL && oldlookup->need_search) { + newlookup = requeue_lookup(oldlookup, true); + newlookup->origin = ISC_LIST_HEAD(search_list); + newlookup->need_search = false; + } else { + search = ISC_LIST_NEXT(oldlookup->origin, link); + if (search == NULL && oldlookup->done_as_is) { + return (false); + } + newlookup = requeue_lookup(oldlookup, true); + newlookup->origin = search; + } + cancel_lookup(oldlookup); + return (true); +} + +/*% + * Insert an SOA record into the sendmessage in a lookup. Used for + * creating IXFR queries. + */ +static void +insert_soa(dig_lookup_t *lookup) { + isc_result_t result; + dns_rdata_soa_t soa; + dns_rdata_t *rdata = NULL; + dns_rdatalist_t *rdatalist = NULL; + dns_rdataset_t *rdataset = NULL; + dns_name_t *soaname = NULL; + + debug("insert_soa()"); + soa.mctx = mctx; + soa.serial = lookup->ixfr_serial; + soa.refresh = 0; + soa.retry = 0; + soa.expire = 0; + soa.minimum = 0; + soa.common.rdclass = lookup->rdclass; + soa.common.rdtype = dns_rdatatype_soa; + + dns_name_init(&soa.origin, NULL); + dns_name_init(&soa.contact, NULL); + + dns_name_clone(dns_rootname, &soa.origin); + dns_name_clone(dns_rootname, &soa.contact); + + isc_buffer_init(&lookup->rdatabuf, lookup->rdatastore, + sizeof(lookup->rdatastore)); + + result = dns_message_gettemprdata(lookup->sendmsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + result = dns_rdata_fromstruct(rdata, lookup->rdclass, dns_rdatatype_soa, + &soa, &lookup->rdatabuf); + check_result(result, "isc_rdata_fromstruct"); + + result = dns_message_gettemprdatalist(lookup->sendmsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + + result = dns_message_gettemprdataset(lookup->sendmsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + + dns_rdatalist_init(rdatalist); + rdatalist->type = dns_rdatatype_soa; + rdatalist->rdclass = lookup->rdclass; + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + + dns_rdatalist_tordataset(rdatalist, rdataset); + + result = dns_message_gettempname(lookup->sendmsg, &soaname); + check_result(result, "dns_message_gettempname"); + dns_name_clone(lookup->name, soaname); + ISC_LIST_INIT(soaname->list); + ISC_LIST_APPEND(soaname->list, rdataset, link); + dns_message_addname(lookup->sendmsg, soaname, DNS_SECTION_AUTHORITY); +} + +static void +compute_cookie(unsigned char *clientcookie, size_t len) { + /* XXXMPA need to fix, should be per server. */ + INSIST(len >= 8U); + memmove(clientcookie, cookie_secret, 8); +} + +#define new_query(l, s, u) _new_query(l, s, u, __FILE__, __LINE__) + +static dig_query_t * +_new_query(dig_lookup_t *lookup, char *servname, char *userarg, + const char *file, unsigned int line) { + dig_query_t *query = NULL; + + query = isc_mem_allocate(mctx, sizeof(dig_query_t)); + debug("create query %p linked to lookup %p", query, lookup); + *query = (dig_query_t){ .sendbuf = lookup->renderbuf, + .servname = servname, + .userarg = userarg, + .warn_id = true, + .recvspace = isc_mem_get(mctx, COMMSIZE), + .tmpsendspace = isc_mem_get(mctx, COMMSIZE) }; + + lookup_attach(lookup, &query->lookup); + + isc_refcount_init(&query->references, 1); + + debug("%s:%u:new_query(%p) = %" PRIuFAST32, file, line, query, + isc_refcount_current(&query->references)); + + if (query->recvspace == NULL) { + fatal("memory allocation failure"); + } + if (query->tmpsendspace == NULL) { + fatal("memory allocation failure"); + } + + isc_time_settoepoch(&query->time_sent); + isc_time_settoepoch(&query->time_recv); + + ISC_LINK_INIT(query, clink); + ISC_LINK_INIT(query, link); + + query->magic = DIG_QUERY_MAGIC; + return (query); +} + +/*% + * Setup the supplied lookup structure, making it ready to start sending + * queries to servers. Create and initialize the message to be sent as + * well as the query structures and buffer space for the replies. If the + * server list is empty, clone it from the system default list. + */ +bool +setup_lookup(dig_lookup_t *lookup) { + isc_result_t result; + unsigned int len; + dig_server_t *serv; + dig_query_t *query; + isc_buffer_t b; + dns_compress_t cctx; + char store[MXNAME]; + char ecsbuf[20]; + char cookiebuf[256]; + char *origin = NULL; + char *textname = NULL; + + REQUIRE(lookup != NULL); + +#ifdef HAVE_LIBIDN2 + char idn_origin[MXNAME], idn_textname[MXNAME]; +#endif /* HAVE_LIBIDN2 */ + + INSIST(!free_now); + + debug("setup_lookup(%p)", lookup); + + dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &lookup->sendmsg); + + if (lookup->new_search) { + debug("resetting lookup counter."); + lookup_counter = 0; + } + + if (ISC_LIST_EMPTY(lookup->my_server_list)) { + debug("cloning server list"); + clone_server_list(server_list, &lookup->my_server_list); + } + result = dns_message_gettempname(lookup->sendmsg, &lookup->name); + check_result(result, "dns_message_gettempname"); + + isc_buffer_init(&lookup->namebuf, lookup->name_space, + sizeof(lookup->name_space)); + isc_buffer_init(&lookup->onamebuf, lookup->oname_space, + sizeof(lookup->oname_space)); + + /* + * We cannot convert `textname' and `origin' separately. + * `textname' doesn't contain TLD, but local mapping needs + * TLD. + */ + textname = lookup->textname; +#ifdef HAVE_LIBIDN2 + if (lookup->idnin) { + idn_locale_to_ace(textname, idn_textname, sizeof(idn_textname)); + debug("idn_textname: %s", idn_textname); + textname = idn_textname; + } +#endif /* HAVE_LIBIDN2 */ + + /* + * If the name has too many dots, force the origin to be NULL + * (which produces an absolute lookup). Otherwise, take the origin + * we have if there's one in the struct already. If it's NULL, + * take the first entry in the searchlist iff either usesearch + * is TRUE or we got a domain line in the resolv.conf file. + */ + if (lookup->new_search) { + if ((count_dots(textname) >= ndots) || !usesearch) { + lookup->origin = NULL; /* Force abs lookup */ + lookup->done_as_is = true; + lookup->need_search = usesearch; + } else if (lookup->origin == NULL && usesearch) { + lookup->origin = ISC_LIST_HEAD(search_list); + lookup->need_search = false; + } + } + + if (lookup->origin != NULL) { + debug("trying origin %s", lookup->origin->origin); + result = dns_message_gettempname(lookup->sendmsg, + &lookup->oname); + check_result(result, "dns_message_gettempname"); + /* XXX Helper funct to conv char* to name? */ + origin = lookup->origin->origin; +#ifdef HAVE_LIBIDN2 + if (lookup->idnin) { + idn_locale_to_ace(origin, idn_origin, + sizeof(idn_origin)); + debug("trying idn origin %s", idn_origin); + origin = idn_origin; + } +#endif /* HAVE_LIBIDN2 */ + len = (unsigned int)strlen(origin); + isc_buffer_init(&b, origin, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(lookup->oname, &b, dns_rootname, 0, + &lookup->onamebuf); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, &lookup->name); + dns_message_puttempname(lookup->sendmsg, + &lookup->oname); + fatal("'%s' is not in legal name syntax (%s)", origin, + isc_result_totext(result)); + } + if (lookup->trace && lookup->trace_root) { + dns_name_clone(dns_rootname, lookup->name); + } else { + dns_fixedname_t fixed; + dns_name_t *name; + + name = dns_fixedname_initname(&fixed); + len = (unsigned int)strlen(textname); + isc_buffer_init(&b, textname, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(name, &b, NULL, 0, NULL); + if (result == ISC_R_SUCCESS) { + if (!dns_name_isabsolute(name)) { + result = dns_name_concatenate( + name, lookup->oname, + lookup->name, &lookup->namebuf); + } else { + dns_name_copy(name, lookup->name); + } + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, + &lookup->name); + dns_message_puttempname(lookup->sendmsg, + &lookup->oname); + if (result == DNS_R_NAMETOOLONG) { + return (false); + } + fatal("'%s' is not in legal name syntax (%s)", + lookup->textname, + isc_result_totext(result)); + } + } + dns_message_puttempname(lookup->sendmsg, &lookup->oname); + } else { + debug("using root origin"); + if (lookup->trace && lookup->trace_root) { + dns_name_clone(dns_rootname, lookup->name); + } else { + len = (unsigned int)strlen(textname); + isc_buffer_init(&b, textname, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(lookup->name, &b, + dns_rootname, 0, + &lookup->namebuf); + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, &lookup->name); + warn("'%s' is not a legal name " + "(%s)", + lookup->textname, isc_result_totext(result)); +#if TARGET_OS_IPHONE + clear_current_lookup(); + return (false); +#else /* if TARGET_OS_IPHONE */ + cleanup_openssl_refs(); + digexit(); +#endif /* if TARGET_OS_IPHONE */ + } + } + dns_name_format(lookup->name, store, sizeof(store)); + dighost_trying(store, lookup); + INSIST(dns_name_isabsolute(lookup->name)); + + lookup->sendmsg->id = (dns_messageid_t)isc_random16(); + lookup->sendmsg->opcode = lookup->opcode; + lookup->msgcounter = 0; + + /* + * If this is a trace request, completely disallow recursion after + * looking up the root name servers, since it's meaningless for traces. + */ + if ((lookup->trace || lookup->ns_search_only) && !lookup->trace_root) { + lookup->recurse = false; + } + + if (lookup->recurse && lookup->rdtype != dns_rdatatype_axfr && + lookup->rdtype != dns_rdatatype_ixfr) + { + debug("recursive query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RD; + } + + /* XXX aaflag */ + if (lookup->aaonly) { + debug("AA query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AA; + } + + if (lookup->adflag) { + debug("AD query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AD; + } + + if (lookup->cdflag) { + debug("CD query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_CD; + } + + if (lookup->raflag) { + debug("RA query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RA; + } + + if (lookup->tcflag) { + debug("TC query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_TC; + } + + if (lookup->zflag) { + debug("Z query"); + lookup->sendmsg->flags |= 0x0040U; + } + + if (lookup->setqid) { + debug("set QID"); + lookup->sendmsg->id = lookup->qid; + } + + dns_message_addname(lookup->sendmsg, lookup->name, + DNS_SECTION_QUESTION); + + if (lookup->trace && lookup->trace_root) { + lookup->qrdtype = lookup->rdtype; + lookup->rdtype = dns_rdatatype_ns; + } + + if ((lookup->rdtype == dns_rdatatype_axfr) || + (lookup->rdtype == dns_rdatatype_ixfr)) + { + /* + * Force TCP mode if we're doing an axfr. + */ + if (lookup->rdtype == dns_rdatatype_axfr) { + lookup->doing_xfr = true; + lookup->tcp_mode = true; + } else if (lookup->tcp_mode) { + lookup->doing_xfr = true; + } + } + + if (!lookup->header_only) { + add_question(lookup->sendmsg, lookup->name, lookup->rdclass, + lookup->rdtype); + } + + /* add_soa */ + if (lookup->rdtype == dns_rdatatype_ixfr) { + insert_soa(lookup); + } + + /* XXX Insist this? */ + lookup->tsigctx = NULL; + lookup->querysig = NULL; + if (tsigkey != NULL) { + debug("initializing keys"); + result = dns_message_settsigkey(lookup->sendmsg, tsigkey); + check_result(result, "dns_message_settsigkey"); + } else if (sig0key != NULL) { + debug("initializing keys"); + result = dns_message_setsig0key(lookup->sendmsg, sig0key); + check_result(result, "dns_message_setsig0key"); + } + + if (lookup->fuzzing) { + lookup->sendmsg->fuzzing = true; + lookup->sendmsg->fuzztime = lookup->fuzztime; + } + + lookup->sendspace = isc_mem_get(mctx, COMMSIZE); + + result = dns_compress_init(&cctx, -1, mctx); + check_result(result, "dns_compress_init"); + + debug("starting to render the message"); + isc_buffer_init(&lookup->renderbuf, lookup->sendspace, COMMSIZE); + result = dns_message_renderbegin(lookup->sendmsg, &cctx, + &lookup->renderbuf); + check_result(result, "dns_message_renderbegin"); + if (lookup->udpsize > -1 || lookup->dnssec || lookup->edns > -1 || + lookup->ecs_addr != NULL) + { +#define MAXOPTS (EDNSOPT_OPTIONS + DNS_EDNSOPTIONS) + dns_ednsopt_t opts[MAXOPTS]; + unsigned int flags; + unsigned int i = 0; + + /* + * There can't be more than MAXOPTS options to send: + * a maximum of EDNSOPT_OPTIONS set by +ednsopt + * and DNS_EDNSOPTIONS set by other arguments + * (+nsid, +cookie, etc). + */ + if (lookup->udpsize < 0) { + lookup->udpsize = DEFAULT_EDNS_BUFSIZE; + } + if (lookup->edns < 0) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + + if (lookup->nsid) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_NSID; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->ecs_addr != NULL) { + uint8_t addr[16]; + uint16_t family = 0; + uint32_t plen; + struct sockaddr *sa; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + size_t addrl; + + sa = &lookup->ecs_addr->type.sa; + plen = lookup->ecs_addr->length; + + /* Round up prefix len to a multiple of 8 */ + addrl = (plen + 7) / 8; + + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_CLIENT_SUBNET; + opts[i].length = (uint16_t)addrl + 4; + check_result(result, "isc_buffer_allocate"); + + /* + * XXXMUKS: According to RFC7871, "If there is + * no ADDRESS set, i.e., SOURCE PREFIX-LENGTH is + * set to 0, then FAMILY SHOULD be set to the + * transport over which the query is sent." + * + * However, at this point we don't know what + * transport(s) we'll be using, so we can't + * set the value now. For now, we're using + * IPv4 as the default the +subnet option + * used an IPv4 prefix, or for +subnet=0, + * and IPv6 if the +subnet option used an + * IPv6 prefix. + * + * (For future work: preserve the offset into + * the buffer where the family field is; + * that way we can update it in start_udp() + * or start_tcp() once we know + * what it outght to be.) + */ + switch (sa->sa_family) { + case AF_UNSPEC: + INSIST(plen == 0); + family = 1; + break; + case AF_INET: + INSIST(plen <= 32); + family = 1; + sin = (struct sockaddr_in *)sa; + memmove(addr, &sin->sin_addr, addrl); + break; + case AF_INET6: + INSIST(plen <= 128); + family = 2; + sin6 = (struct sockaddr_in6 *)sa; + memmove(addr, &sin6->sin6_addr, addrl); + break; + default: + UNREACHABLE(); + } + + isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); + /* family */ + isc_buffer_putuint16(&b, family); + /* source prefix-length */ + isc_buffer_putuint8(&b, plen); + /* scope prefix-length */ + isc_buffer_putuint8(&b, 0); + + /* address */ + if (addrl > 0) { + /* Mask off last address byte */ + if ((plen % 8) != 0) { + addr[addrl - 1] &= ~0U + << (8 - (plen % 8)); + } + isc_buffer_putmem(&b, addr, (unsigned)addrl); + } + + opts[i].value = (uint8_t *)ecsbuf; + i++; + } + + if (lookup->sendcookie) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_COOKIE; + if (lookup->cookie != NULL) { + isc_buffer_init(&b, cookiebuf, + sizeof(cookiebuf)); + result = isc_hex_decodestring(lookup->cookie, + &b); + check_result(result, "isc_hex_decodestring"); + opts[i].value = isc_buffer_base(&b); + opts[i].length = isc_buffer_usedlength(&b); + } else { + compute_cookie(cookie, sizeof(cookie)); + opts[i].length = 8; + opts[i].value = cookie; + } + i++; + } + + if (lookup->expire) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_EXPIRE; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->tcp_keepalive) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_TCP_KEEPALIVE; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->ednsoptscnt != 0) { + INSIST(i + lookup->ednsoptscnt <= MAXOPTS); + memmove(&opts[i], lookup->ednsopts, + sizeof(dns_ednsopt_t) * lookup->ednsoptscnt); + i += lookup->ednsoptscnt; + } + + if (lookup->padding != 0 && (i >= MAXOPTS)) { + debug("turned off padding because of EDNS overflow"); + lookup->padding = 0; + } + + if (lookup->padding != 0) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_PAD; + opts[i].length = 0; + opts[i].value = NULL; + i++; + dns_message_setpadding(lookup->sendmsg, + lookup->padding); + } + + flags = lookup->ednsflags; + flags &= ~DNS_MESSAGEEXTFLAG_DO; + if (lookup->dnssec) { + flags |= DNS_MESSAGEEXTFLAG_DO; + } + add_opt(lookup->sendmsg, lookup->udpsize, lookup->edns, flags, + opts, i); + } + + result = dns_message_rendersection(lookup->sendmsg, + DNS_SECTION_QUESTION, 0); + check_result(result, "dns_message_rendersection"); + result = dns_message_rendersection(lookup->sendmsg, + DNS_SECTION_AUTHORITY, 0); + check_result(result, "dns_message_rendersection"); + result = dns_message_renderend(lookup->sendmsg); + check_result(result, "dns_message_renderend"); + debug("done rendering"); + + dns_compress_invalidate(&cctx); + + /* + * Force TCP mode if the request is larger than 512 bytes. + */ + if (isc_buffer_usedlength(&lookup->renderbuf) > 512) { + lookup->tcp_mode = true; + } + + lookup->pending = false; + + for (serv = ISC_LIST_HEAD(lookup->my_server_list); serv != NULL; + serv = ISC_LIST_NEXT(serv, link)) + { + query = new_query(lookup, serv->servername, serv->userarg); + ISC_LIST_ENQUEUE(lookup->q, query, link); + } + + return (true); +} + +/*% + * NSSEARCH mode special mode handling function to start the next query in the + * list. The lookup lock must be held by the caller. The function will detach + * both the lookup and the query, and may cancel the lookup and clear the + * current lookup. + */ +static void +nssearch_next(dig_lookup_t *l, dig_query_t *q) { + dig_query_t *next = ISC_LIST_NEXT(q, link); + bool tcp_mode = l->tcp_mode; + + INSIST(l->ns_search_only && !l->trace_root); + INSIST(l == current_lookup); + + if (next == NULL) { + /* + * If this is the last query, and if there was + * not a single successful query in the whole + * lookup, then treat the situation as an error, + * cancel and clear the lookup. + */ + if (check_if_queries_done(l, q) && !l->ns_search_success) { + dighost_error("NS servers could not be reached"); + if (exitcode < 9) { + exitcode = 9; + } + + cancel_lookup(l); + query_detach(&q); + lookup_detach(&l); + clear_current_lookup(); + } else { + query_detach(&q); + lookup_detach(&l); + } + } else { + query_detach(&q); + lookup_detach(&l); + + debug("sending next, since searching"); + if (tcp_mode) { + start_tcp(next); + } else { + start_udp(next); + } + } +} + +/*% + * Event handler for send completion. Track send counter, and clear out + * the query if the send was canceled. + */ +static void +send_done(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { + dig_query_t *query = (dig_query_t *)arg; + dig_lookup_t *l = NULL; + + REQUIRE(DIG_VALID_QUERY(query)); + INSIST(query->sendhandle != NULL); + INSIST(handle == query->sendhandle); + + debug("send_done(%p, %s, %p)", handle, isc_result_totext(eresult), arg); + + isc_refcount_decrement0(&sendcount); + debug("sendcount=%" PRIuFAST32, isc_refcount_current(&sendcount)); + + INSIST(!free_now); + + LOCK_LOOKUP; + + isc_nmhandle_detach(&query->sendhandle); + + lookup_attach(query->lookup, &l); + + if (eresult == ISC_R_CANCELED || query->canceled) { + debug("send_done: cancel"); + if (!query->canceled) { + cancel_lookup(l); + } + query_detach(&query); + lookup_detach(&l); + UNLOCK_LOOKUP; + return; + } else if (eresult != ISC_R_SUCCESS) { + debug("send failed: %s", isc_result_totext(eresult)); + } + + if (l->ns_search_only && !l->trace_root) { + nssearch_next(l, query); + } else { + query_detach(&query); + lookup_detach(&l); + } + + check_if_done(); + UNLOCK_LOOKUP; +} + +/*% + * Cancel a lookup, sending canceling reads on all existing sockets. + */ + +static void +_cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) { + dig_query_t *query, *next; + + debug("%s:%u:%s()", file, line, __func__); + query = ISC_LIST_HEAD(lookup->q); + while (query != NULL) { + REQUIRE(DIG_VALID_QUERY(query)); + next = ISC_LIST_NEXT(query, link); + ISC_LIST_DEQUEUE(lookup->q, query, link); + debug("canceling pending query %p, belonging to %p", query, + query->lookup); + query->canceled = true; + if (query->readhandle != NULL && + !isc_nm_is_http_handle(query->readhandle)) + { + isc_nm_cancelread(query->readhandle); + } + query_detach(&query); + query = next; + } + lookup->pending = false; + lookup->retries = 0; + check_if_done(); +} + +static isc_tlsctx_t * +get_create_tls_context(dig_query_t *query, const bool is_https, + isc_tlsctx_client_session_cache_t **psess_cache) { + isc_result_t result; + isc_tlsctx_t *ctx = NULL, *found_ctx = NULL; + isc_tls_cert_store_t *store = NULL, *found_store = NULL; + char tlsctxname[ISC_SOCKADDR_FORMATSIZE]; + const uint16_t family = isc_sockaddr_pf(&query->sockaddr) == PF_INET6 + ? AF_INET6 + : AF_INET; + isc_tlsctx_cache_transport_t transport = + is_https ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls; + const bool hostname_ignore_subject = !is_https; + isc_tlsctx_client_session_cache_t *sess_cache = NULL, + *found_sess_cache = NULL; + + if (query->lookup->tls_key_file_set != query->lookup->tls_cert_file_set) + { + return (NULL); + } + + isc_sockaddr_format(&query->sockaddr, tlsctxname, sizeof(tlsctxname)); + + result = isc_tlsctx_cache_find(query->lookup->tls_ctx_cache, tlsctxname, + transport, family, &found_ctx, + &found_store, &found_sess_cache); + if (result != ISC_R_SUCCESS) { + if (query->lookup->tls_ca_set) { + if (found_store == NULL) { + result = isc_tls_cert_store_create( + query->lookup->tls_ca_file, &store); + + if (result != ISC_R_SUCCESS) { + goto failure; + } + } else { + store = found_store; + } + } + + result = isc_tlsctx_createclient(&ctx); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + if (store != NULL) { + const char *hostname = + query->lookup->tls_hostname_set + ? query->lookup->tls_hostname + : query->userarg; + /* + * According to RFC 8310, Subject field MUST NOT be + * inspected when verifying hostname for DoT. Only + * SubjectAltName must be checked. That is NOT the case + * for HTTPS. + */ + result = isc_tlsctx_enable_peer_verification( + ctx, false, store, hostname, + hostname_ignore_subject); + if (result != ISC_R_SUCCESS) { + goto failure; + } + } + + if (query->lookup->tls_key_file_set && + query->lookup->tls_cert_file_set) + { + result = isc_tlsctx_load_certificate( + ctx, query->lookup->tls_key_file, + query->lookup->tls_cert_file); + if (result != ISC_R_SUCCESS) { + goto failure; + } + } + + if (!is_https) { + isc_tlsctx_enable_dot_client_alpn(ctx); + } + +#if HAVE_LIBNGHTTP2 + if (is_https) { + isc_tlsctx_enable_http2client_alpn(ctx); + } +#endif /* HAVE_LIBNGHTTP2 */ + + isc_tlsctx_client_session_cache_create( + mctx, ctx, ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE, + &sess_cache); + + result = isc_tlsctx_cache_add( + query->lookup->tls_ctx_cache, tlsctxname, transport, + family, ctx, store, sess_cache, NULL, NULL, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (psess_cache != NULL) { + INSIST(*psess_cache == NULL); + *psess_cache = sess_cache; + } + return (ctx); + } + + if (psess_cache != NULL) { + INSIST(*psess_cache == NULL); + *psess_cache = found_sess_cache; + } + + INSIST(!query->lookup->tls_ca_set || found_store != NULL); + return (found_ctx); +failure: + if (ctx != NULL) { + isc_tlsctx_free(&ctx); + } + /* + * The 'found_store' is being managed by the TLS context + * cache. Thus, we should keep it as it is, as it will get + * destroyed alongside the cache. As there is one store per + * multiple TLS contexts, we need to handle store deletion in a + * special way. + */ + if (store != NULL && store != found_store) { + isc_tls_cert_store_free(&store); + } + return (NULL); +} + +static void +tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg); + +/*% + * Unlike start_udp, this can't be called multiple times with the same + * query. When we retry TCP, we requeue the whole lookup, which should + * start anew. + */ +static void +start_tcp(dig_query_t *query) { + isc_result_t result; + dig_query_t *next = NULL; + dig_query_t *connectquery = NULL; + isc_tlsctx_t *tlsctx = NULL; + bool tls_mode = false; + isc_tlsctx_client_session_cache_t *sess_cache = NULL; + REQUIRE(DIG_VALID_QUERY(query)); + + debug("start_tcp(%p)", query); + + query_attach(query, &query->lookup->current_query); + + tls_mode = dig_lookup_is_tls(query->lookup); + + /* + * For TLS connections, we want to override the default + * port number. + */ + if (!port_set) { + if (tls_mode) { + port = 853; + } else if (query->lookup->https_mode && + !query->lookup->http_plain) + { + port = 443; + } else if (query->lookup->https_mode) { + port = 80; + } else { + port = 53; + } + } + + debug("query->servname = %s\n", query->servname); + + result = get_address(query->servname, port, &query->sockaddr); + if (result != ISC_R_SUCCESS) { + /* + * This servname doesn't have an address. Try the next server + * by triggering an immediate 'timeout' (we lie, but the effect + * is the same). + */ + force_next(query); + return; + } + + if (isc_sockaddr_pf(&query->sockaddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr)) + { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + dighost_warning("Skipping mapped address '%s'", buf); + + if (ISC_LINK_LINKED(query, link)) { + next = ISC_LIST_NEXT(query, link); + } else { + next = NULL; + } + query_detach(&query); + if (next == NULL) { + dighost_warning("No acceptable nameservers"); + clear_current_lookup(); + } else { + start_tcp(next); + } + return; + } + + INSIST(query->handle == NULL); + + if (keep != NULL && isc_sockaddr_equal(&keepaddr, &query->sockaddr)) { + query->handle = keep; + launch_next_query(query); + query_detach(&query); + return; + } else { + int local_timeout = timeout * 1000; + if (local_timeout == 0) { + local_timeout = TCP_TIMEOUT * 1000; + } + + if (keep != NULL) { + isc_nmhandle_detach(&keep); + } + + if (!specified_source) { + if ((isc_sockaddr_pf(&query->sockaddr) == AF_INET) && + have_ipv4) + { + isc_sockaddr_any(&localaddr); + } else { + isc_sockaddr_any6(&localaddr); + } + } + + REQUIRE(query != NULL); + + query_attach(query, &connectquery); + + if (tls_mode) { + tlsctx = get_create_tls_context(connectquery, false, + &sess_cache); + if (tlsctx == NULL) { + goto failure_tls; + } + isc_nm_tlsdnsconnect(netmgr, &localaddr, + &query->sockaddr, tcp_connected, + connectquery, local_timeout, 0, + tlsctx, sess_cache); +#if HAVE_LIBNGHTTP2 + } else if (query->lookup->https_mode) { + char uri[4096] = { 0 }; + isc_nm_http_makeuri(!query->lookup->http_plain, + &query->sockaddr, query->userarg, + port, query->lookup->https_path, + uri, sizeof(uri)); + + if (!query->lookup->http_plain) { + tlsctx = get_create_tls_context( + connectquery, true, &sess_cache); + if (tlsctx == NULL) { + goto failure_tls; + } + } + + isc_nm_httpconnect(netmgr, &localaddr, &query->sockaddr, + uri, !query->lookup->https_get, + tcp_connected, connectquery, tlsctx, + sess_cache, 0, local_timeout); +#endif + } else { + isc_nm_tcpdnsconnect(netmgr, &localaddr, + &query->sockaddr, tcp_connected, + connectquery, local_timeout, 0); + } + } + return; +failure_tls: + if (query->lookup->tls_key_file_set != query->lookup->tls_cert_file_set) + { + dighost_warning( + "both TLS client certificate and key file must be " + "specified a the same time"); + } else { + dighost_warning("TLS context cannot be created"); + } + + if (ISC_LINK_LINKED(query, link)) { + next = ISC_LIST_NEXT(query, link); + } else { + next = NULL; + } + query_detach(&connectquery); + query_detach(&query); + if (next == NULL) { + clear_current_lookup(); + } else { + start_tcp(next); + } +} + +static void +print_query_size(dig_query_t *query) { + if (!yaml) { + printf(";; QUERY SIZE: %u\n\n", + isc_buffer_usedlength(&query->lookup->renderbuf)); + } +} + +static void +send_udp(dig_query_t *query) { + dig_query_t *sendquery = NULL; + isc_region_t r; + + query_attach(query, &sendquery); + + isc_buffer_usedregion(&query->sendbuf, &r); + debug("sending a request"); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_sent); + } else { + TIME_NOW(&query->time_sent); + } + + isc_nmhandle_attach(query->handle, &query->sendhandle); + + isc_nm_send(query->handle, &r, send_done, sendquery); + isc_refcount_increment0(&sendcount); + debug("sendcount=%" PRIuFAST32, isc_refcount_current(&sendcount)); + + /* XXX qrflag, print_query, etc... */ + if (query->lookup->qr) { + extrabytes = 0; + dighost_printmessage(query, &query->lookup->renderbuf, + query->lookup->sendmsg, true); + if (query->lookup->stats) { + print_query_size(query); + } + } +} + +static void +udp_ready(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { + dig_query_t *query = (dig_query_t *)arg; + dig_query_t *next = NULL; + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + dig_lookup_t *l = NULL; + dig_query_t *readquery = NULL; + int local_timeout = timeout * 1000; + + REQUIRE(DIG_VALID_QUERY(query)); + REQUIRE(query->handle == NULL); + + debug("udp_ready()"); + + query->started = true; + + if (atomic_load(&cancel_now)) { + query_detach(&query); + return; + } + + INSIST(!free_now); + + debug("udp_ready(%p, %s, %p)", handle, isc_result_totext(eresult), + query); + + LOCK_LOOKUP; + lookup_attach(query->lookup, &l); + + if (eresult == ISC_R_CANCELED || query->canceled) { + debug("in cancel handler"); + if (!query->canceled) { + cancel_lookup(l); + } + query_detach(&query); + lookup_detach(&l); + clear_current_lookup(); + UNLOCK_LOOKUP; + return; + } + + if (eresult != ISC_R_SUCCESS) { + debug("udp setup failed: %s", isc_result_totext(eresult)); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + dighost_warning("UDP setup with %s(%s) for %s failed: %s.", + sockstr, query->servname, l->textname, + isc_result_totext(eresult)); + + /* + * NSSEARCH mode: if the current query failed to start properly, + * then send_done() will not be called, and we want to make sure + * that the next query gets a chance to start in order to not + * break the chain. + */ + if (l->ns_search_only && !l->trace_root) { + nssearch_next(l, query); + + check_if_done(); + UNLOCK_LOOKUP; + return; + } + + if (exitcode < 9) { + exitcode = 9; + } + + if (l->retries > 1) { + l->retries--; + debug("making new UDP request, %d tries left", + l->retries); + requeue_lookup(l, true); + next = NULL; + } else if ((l->current_query != NULL) && + (ISC_LINK_LINKED(l->current_query, link))) + { + next = ISC_LIST_NEXT(l->current_query, link); + } else { + next = NULL; + } + + query_detach(&query); + if (next == NULL) { + cancel_lookup(l); + } + lookup_detach(&l); + + if (next != NULL) { + start_udp(next); + check_if_done(); + } else { + clear_current_lookup(); + } + + UNLOCK_LOOKUP; + return; + } + + exitcode = 0; + + query_attach(query, &readquery); + + debug("recving with lookup=%p, query=%p, handle=%p", query->lookup, + query, handle); + + query->handle = handle; + isc_nmhandle_attach(handle, &query->readhandle); + isc_refcount_increment0(&recvcount); + debug("recvcount=%" PRIuFAST32, isc_refcount_current(&recvcount)); + + if (local_timeout == 0) { + local_timeout = UDP_TIMEOUT * 1000; + } + + debug("have local timeout of %d", local_timeout); + isc_nmhandle_settimeout(handle, local_timeout); + + isc_nm_read(handle, recv_done, readquery); + send_udp(readquery); + + query_detach(&query); + lookup_detach(&l); + UNLOCK_LOOKUP; +} + +/*% + * Send a UDP packet to the remote nameserver, possible starting the + * recv action as well. Also make sure that the timer is running and + * is properly reset. + */ +static void +start_udp(dig_query_t *query) { + isc_result_t result; + dig_query_t *next = NULL; + dig_query_t *connectquery = NULL; + + REQUIRE(DIG_VALID_QUERY(query)); + + debug("start_udp(%p)", query); + + query_attach(query, &query->lookup->current_query); + debug("working on lookup %p, query %p", query->lookup, query); + + if (query->handle != NULL) { + launch_next_query(query); + query_detach(&query); + return; + } + + result = get_address(query->servname, port, &query->sockaddr); + if (result != ISC_R_SUCCESS) { + /* This servname doesn't have an address. */ + force_next(query); + return; + } + + if (isc_sockaddr_pf(&query->sockaddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr)) + { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + dighost_warning("Skipping mapped address '%s'", buf); + next = ISC_LIST_NEXT(query, link); + query_detach(&query); + if (next == NULL) { + dighost_warning("No acceptable nameservers"); + clear_current_lookup(); + } else { + start_udp(next); + } + return; + } + + if (!specified_source) { + if ((isc_sockaddr_pf(&query->sockaddr) == AF_INET) && have_ipv4) + { + isc_sockaddr_any(&localaddr); + } else { + isc_sockaddr_any6(&localaddr); + } + } + + query_attach(query, &connectquery); + isc_nm_udpconnect(netmgr, &localaddr, &query->sockaddr, udp_ready, + connectquery, + (timeout ? timeout : UDP_TIMEOUT) * 1000, 0); +} + +/*% + * If there are more servers available for querying within 'lookup', initiate a + * TCP or UDP query to the next available server and return true; otherwise, + * return false. + */ +static bool +try_next_server(dig_lookup_t *lookup) { + dig_query_t *current_query, *next_query; + + current_query = lookup->current_query; + if (current_query == NULL || !ISC_LINK_LINKED(current_query, link)) { + return (false); + } + + next_query = ISC_LIST_NEXT(current_query, link); + if (next_query == NULL) { + return (false); + } + + debug("trying next server..."); + + if (lookup->tcp_mode) { + start_tcp(next_query); + } else { + start_udp(next_query); + } + + return (true); +} + +static void +force_next(dig_query_t *query) { + dig_lookup_t *l = NULL; + + REQUIRE(DIG_VALID_QUERY(query)); + + debug("force_next()"); + + LOCK_LOOKUP; + INSIST(!free_now); + + if (atomic_load(&cancel_now)) { + UNLOCK_LOOKUP; + return; + } + + lookup_attach(query->lookup, &l); + + if (try_next_server(l)) { + lookup_detach(&l); + UNLOCK_LOOKUP; + return; + } + + if (l->retries > 1) { + l->retries--; + debug("making new %s request, %d tries left", + l->tcp_mode ? "TCP" : "UDP", l->retries); + requeue_lookup(l, true); + lookup_detach(&l); + isc_refcount_decrement0(&recvcount); + debug("recvcount=%" PRIuFAST32, + isc_refcount_current(&recvcount)); + query_detach(&query); + clear_current_lookup(); + UNLOCK_LOOKUP; + return; + } + + if (query->readhandle != NULL) { + isc_refcount_decrement0(&recvcount); + debug("recvcount=%" PRIuFAST32, + isc_refcount_current(&recvcount)); + } + + if (l->ns_search_only) { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + + dighost_error("no response from %s\n", buf); + } else { + printf("%s", l->cmdline); + dighost_error("no servers could be reached\n"); + } + + if (exitcode < 9) { + exitcode = 9; + } + + query_detach(&query); + cancel_lookup(l); + lookup_detach(&l); + clear_current_lookup(); + UNLOCK_LOOKUP; +} + +/*% + * For transfers that involve multiple recvs (XFR's in particular), + * launch the next recv. + */ +static void +launch_next_query(dig_query_t *query) { + dig_query_t *readquery = NULL; + int local_timeout = timeout * 1000; + dig_lookup_t *l = NULL; + isc_region_t r; + bool xfr; + + REQUIRE(DIG_VALID_QUERY(query)); + INSIST(!free_now); + + debug("launch_next_query()"); + + lookup_attach(query->lookup, &l); + + if (!l->pending) { + debug("ignoring launch_next_query because !pending"); + query_detach(&query); + lookup_detach(&l); + clear_current_lookup(); + return; + } + + isc_nmhandle_attach(query->handle, &query->readhandle); + isc_refcount_increment0(&recvcount); + debug("recvcount=%" PRIuFAST32, isc_refcount_current(&recvcount)); + + if (local_timeout == 0) { + local_timeout = TCP_TIMEOUT * 1000; + } + + debug("have local timeout of %d", local_timeout); + isc_nmhandle_settimeout(query->handle, local_timeout); + + xfr = query->lookup->rdtype == dns_rdatatype_ixfr || + query->lookup->rdtype == dns_rdatatype_axfr; + if (xfr && isc_nm_socket_type(query->handle) == isc_nm_tlsdnssocket) { + isc_result_t result = isc_nm_xfr_checkperm(query->handle); + if (result != ISC_R_SUCCESS) { + dighost_error("zone transfers over the established TLS " + "connection are not allowed: %s", + isc_result_totext(result)); + isc_refcount_decrement0(&recvcount); + isc_nmhandle_detach(&query->readhandle); + cancel_lookup(l); + lookup_detach(&l); + clear_current_lookup(); + return; + } + } + + query_attach(query, &readquery); + + isc_nm_read(query->handle, recv_done, readquery); + + if (!query->first_soa_rcvd) { + dig_query_t *sendquery = NULL; + debug("sending a request in launch_next_query"); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_sent); + } else { + TIME_NOW(&query->time_sent); + } + + query_attach(query, &sendquery); + isc_buffer_usedregion(&query->sendbuf, &r); + if (keep != NULL) { + query->handle = keep; + } + + isc_nmhandle_attach(query->handle, &query->sendhandle); + isc_nm_send(query->handle, &r, send_done, sendquery); + isc_refcount_increment0(&sendcount); + debug("sendcount=%" PRIuFAST32, + isc_refcount_current(&sendcount)); + + /* XXX qrflag, print_query, etc... */ + if (l->qr) { + extrabytes = 0; + dighost_printmessage(query, &l->renderbuf, l->sendmsg, + true); + if (l->stats) { + print_query_size(query); + } + } + } + + lookup_detach(&l); + return; +} + +/*% + * Event handler for TCP connect complete. Make sure the connection was + * successful, then pass into launch_next_query to actually send the + * question. + */ +static void +tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { + dig_query_t *query = (dig_query_t *)arg; + dig_query_t *next = NULL; + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + dig_lookup_t *l = NULL; + + REQUIRE(DIG_VALID_QUERY(query)); + REQUIRE(query->handle == NULL); + + debug("tcp_connected()"); + + query->started = true; + + if (atomic_load(&cancel_now)) { + query_detach(&query); + return; + } + + INSIST(!free_now); + + debug("tcp_connected(%p, %s, %p)", handle, isc_result_totext(eresult), + query); + + LOCK_LOOKUP; + lookup_attach(query->lookup, &l); + + if (eresult == ISC_R_CANCELED || eresult == ISC_R_TLSBADPEERCERT || + query->canceled) + { + debug("in cancel handler"); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + if (eresult == ISC_R_TLSBADPEERCERT) { + dighost_warning( + "TLS peer certificate verification for " + "%s failed: %s", + sockstr, + isc_nm_verify_tls_peer_result_string(handle)); + } else if (query->lookup->rdtype == dns_rdatatype_ixfr || + query->lookup->rdtype == dns_rdatatype_axfr) + { + puts("; Transfer failed."); + } + + if (!query->canceled) { + cancel_lookup(l); + } + + query_detach(&query); + lookup_detach(&l); + clear_current_lookup(); + UNLOCK_LOOKUP; + return; + } + + if (eresult != ISC_R_SUCCESS) { + debug("unsuccessful connection: %s", + isc_result_totext(eresult)); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + dighost_warning("Connection to %s(%s) for %s failed: %s.", + sockstr, query->servname, l->textname, + isc_result_totext(eresult)); + + /* + * NSSEARCH mode: if the current query failed to start properly, + * then send_done() will not be called, and we want to make sure + * that the next query gets a chance to start in order to not + * break the chain. + */ + if (l->ns_search_only && !l->trace_root) { + nssearch_next(l, query); + + check_if_done(); + UNLOCK_LOOKUP; + return; + } + + /* XXX Clean up exitcodes */ + if (exitcode < 9) { + exitcode = 9; + } + + if (l->retries > 1) { + l->retries--; + debug("making new TCP request, %d tries left", + l->retries); + requeue_lookup(l, true); + next = NULL; + } else if ((l->current_query != NULL) && + (ISC_LINK_LINKED(l->current_query, link))) + { + next = ISC_LIST_NEXT(l->current_query, link); + } else { + next = NULL; + } + + query_detach(&query); + if (next == NULL) { + cancel_lookup(l); + } + lookup_detach(&l); + + if (next != NULL) { + start_tcp(next); + check_if_done(); + } else { + clear_current_lookup(); + } + + UNLOCK_LOOKUP; + return; + } + + exitcode = 0; + + query->handle = handle; + if (keep_open) { + keepaddr = query->sockaddr; + if (keep != NULL) { + isc_nmhandle_detach(&keep); + } + + isc_nmhandle_attach(handle, &keep); + } + + launch_next_query(query); + query_detach(&query); + lookup_detach(&l); + UNLOCK_LOOKUP; +} + +/*% + * Check if the ongoing XFR needs more data before it's complete, using + * the semantics of IXFR and AXFR protocols. Much of the complexity of + * this routine comes from determining when an IXFR is complete. + * false means more data is on the way, and the recv has been issued. + */ +static bool +check_for_more_data(dig_lookup_t *lookup, dig_query_t *query, + dns_message_t *msg, isc_sockaddr_t *peer, int len) { + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_soa_t soa; + uint32_t ixfr_serial = lookup->ixfr_serial, serial; + isc_result_t result; + bool ixfr = lookup->rdtype == dns_rdatatype_ixfr; + bool axfr = lookup->rdtype == dns_rdatatype_axfr; + + if (ixfr) { + axfr = query->ixfr_axfr; + } + + debug("check_for_more_data()"); + + /* + * By the time we're in this routine, we know we're doing + * either an AXFR or IXFR. If there's no second_rr_type, + * then we don't yet know which kind of answer we got back + * from the server. Here, we're going to walk through the + * rr's in the message, acting as necessary whenever we hit + * an SOA rr. + */ + + query->msg_count++; + query->byte_count += len; + result = dns_message_firstname(msg, DNS_SECTION_ANSWER); + if (result != ISC_R_SUCCESS) { + puts("; Transfer failed."); + return (true); + } + do { + dns_name_t *name; + name = NULL; + dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { + continue; + } + do { + query->rr_count++; + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + /* + * If this is the first rr, make sure + * it's an SOA + */ + if ((!query->first_soa_rcvd) && + (rdata.type != dns_rdatatype_soa)) + { + puts("; Transfer failed. " + "Didn't start with SOA answer."); + return (true); + } + if ((!query->second_rr_rcvd) && + (rdata.type != dns_rdatatype_soa)) + { + query->second_rr_rcvd = true; + query->second_rr_serial = 0; + debug("got the second rr as nonsoa"); + axfr = query->ixfr_axfr = true; + goto next_rdata; + } + + /* + * If the record is anything except an SOA + * now, just continue on... + */ + if (rdata.type != dns_rdatatype_soa) { + goto next_rdata; + } + + /* Now we have an SOA. Work with it. */ + debug("got an SOA"); + result = dns_rdata_tostruct(&rdata, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + serial = soa.serial; + dns_rdata_freestruct(&soa); + if (!query->first_soa_rcvd) { + query->first_soa_rcvd = true; + query->first_rr_serial = serial; + debug("this is the first serial %u", + serial); + if (ixfr && + isc_serial_ge(ixfr_serial, serial)) + { + debug("got up to date " + "response"); + goto doexit; + } + goto next_rdata; + } + if (axfr) { + debug("doing axfr, got second SOA"); + goto doexit; + } + if (!query->second_rr_rcvd) { + if (query->first_rr_serial == serial) { + debug("doing ixfr, got " + "empty zone"); + goto doexit; + } + debug("this is the second serial %u", + serial); + query->second_rr_rcvd = true; + query->second_rr_serial = serial; + goto next_rdata; + } + /* + * If we get to this point, we're doing an + * IXFR and have to start really looking + * at serial numbers. + */ + if (query->first_rr_serial == serial) { + debug("got a match for ixfr"); + if (!query->first_repeat_rcvd) { + query->first_repeat_rcvd = true; + goto next_rdata; + } + debug("done with ixfr"); + goto doexit; + } + debug("meaningless soa %u", serial); + next_rdata: + result = dns_rdataset_next(rdataset); + } while (result == ISC_R_SUCCESS); + } + result = dns_message_nextname(msg, DNS_SECTION_ANSWER); + } while (result == ISC_R_SUCCESS); + isc_nmhandle_detach(&query->readhandle); + launch_next_query(query); + query_detach(&query); + return (false); +doexit: + dighost_received(len, peer, query); + return (true); +} + +static void +process_cookie(dig_lookup_t *l, dns_message_t *msg, isc_buffer_t *optbuf, + size_t optlen) { + char bb[256]; + isc_buffer_t hexbuf; + size_t len; + const unsigned char *sent; + bool copy = true; + isc_result_t result; + + if (l->cookie != NULL) { + isc_buffer_init(&hexbuf, bb, sizeof(bb)); + result = isc_hex_decodestring(l->cookie, &hexbuf); + check_result(result, "isc_hex_decodestring"); + sent = isc_buffer_base(&hexbuf); + len = isc_buffer_usedlength(&hexbuf); + } else { + sent = cookie; + len = sizeof(cookie); + } + + INSIST(msg->cc_ok == 0 && msg->cc_bad == 0); + if (len >= 8 && optlen >= 8U) { + if (isc_safe_memequal(isc_buffer_current(optbuf), sent, 8)) { + msg->cc_ok = 1; + } else { + dighost_warning("Warning: Client COOKIE mismatch"); + msg->cc_bad = 1; + copy = false; + } + } else { + dighost_warning("Warning: COOKIE bad token (too short)"); + msg->cc_bad = 1; + copy = false; + } + if (copy) { + isc_region_t r; + + r.base = isc_buffer_current(optbuf); + r.length = (unsigned int)optlen; + isc_buffer_init(&hexbuf, servercookie, sizeof(servercookie)); + result = isc_hex_totext(&r, 2, "", &hexbuf); + check_result(result, "isc_hex_totext"); + if (isc_buffer_availablelength(&hexbuf) > 0) { + isc_buffer_putuint8(&hexbuf, 0); + l->cookie = servercookie; + } + } + isc_buffer_forward(optbuf, (unsigned int)optlen); +} + +static void +process_opt(dig_lookup_t *l, dns_message_t *msg) { + dns_rdata_t rdata; + isc_result_t result; + isc_buffer_t optbuf; + uint16_t optcode, optlen; + dns_rdataset_t *opt = msg->opt; + bool seen_cookie = false; + + result = dns_rdataset_first(opt); + if (result == ISC_R_SUCCESS) { + dns_rdata_init(&rdata); + dns_rdataset_current(opt, &rdata); + isc_buffer_init(&optbuf, rdata.data, rdata.length); + isc_buffer_add(&optbuf, rdata.length); + while (isc_buffer_remaininglength(&optbuf) >= 4) { + optcode = isc_buffer_getuint16(&optbuf); + optlen = isc_buffer_getuint16(&optbuf); + switch (optcode) { + case DNS_OPT_COOKIE: + /* + * Only process the first cookie option. + */ + if (seen_cookie) { + isc_buffer_forward(&optbuf, optlen); + break; + } + process_cookie(l, msg, &optbuf, optlen); + seen_cookie = true; + break; + default: + isc_buffer_forward(&optbuf, optlen); + break; + } + } + } +} + +static int +ednsvers(dns_rdataset_t *opt) { + return ((opt->ttl >> 16) & 0xff); +} + +/*% + * Event handler for recv complete. Perform whatever actions are necessary, + * based on the specifics of the user's request. + */ +static void +recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, + void *arg) { + dig_query_t *query = (dig_query_t *)arg; + isc_buffer_t b; + dns_message_t *msg = NULL; + isc_result_t result; + dig_lookup_t *n = NULL; + dig_lookup_t *l = NULL; + bool docancel = false; + bool donext = false; + bool match = true; + bool done_process_opt = false; + unsigned int parseflags; + dns_messageid_t id; + unsigned int msgflags; + int newedns; + isc_sockaddr_t peer; + + REQUIRE(DIG_VALID_QUERY(query)); + REQUIRE(query->readhandle != NULL); + INSIST(!free_now); + + debug("recv_done(%p, %s, %p, %p)", handle, isc_result_totext(eresult), + region, arg); + + LOCK_LOOKUP; + + isc_refcount_decrement0(&recvcount); + debug("recvcount=%" PRIuFAST32, isc_refcount_current(&recvcount)); + + lookup_attach(query->lookup, &l); + + if (eresult == ISC_R_CANCELED || query->canceled) { + debug("recv_done: cancel"); + isc_nmhandle_detach(&query->readhandle); + if (!query->canceled) { + cancel_lookup(l); + } + query_detach(&query); + lookup_detach(&l); + clear_current_lookup(); + UNLOCK_LOOKUP; + return; + } + + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_recv); + } else { + TIME_NOW(&query->time_recv); + } + + if ((!l->pending && !l->ns_search_only) || atomic_load(&cancel_now)) { + debug("no longer pending. Got %s", isc_result_totext(eresult)); + + goto next_lookup; + } + + /* + * NSSEARCH mode is special, because the queries in the followup lookup + * are independent and they are being started in parallel, so if one of + * them fails there is no need to start the next query in the lookup, + * and this failure can be treated as a soft error (with a warning + * message), because there are usually more than one NS servers in the + * lookup's queries list. However, if there was not a single successful + * query in the followup lookup, then print an error message and exit + * with a non-zero exit code. + */ + if (l->ns_search_only && !l->trace_root) { + if (eresult == ISC_R_SUCCESS) { + l->ns_search_success = true; + } else { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&query->sockaddr, sockstr, + sizeof(sockstr)); + + dighost_warning("communications error to %s: %s", + sockstr, isc_result_totext(eresult)); + + /* + * If this is not the last query, then we detach the + * query, but keep the lookup running. + */ + if (!check_if_queries_done(l, query)) { + goto detach_query; + } + + /* + * This is the last query, and if there was not a + * single successful query in the whole lookup, then + * treat the situation as an error. + */ + if (!l->ns_search_success) { + dighost_error( + "NS servers could not be reached"); + if (exitcode < 9) { + exitcode = 9; + } + } + + goto cancel_lookup; + } + } + + if (eresult != ISC_R_SUCCESS) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + dighost_warning("communications error to %s: %s", sockstr, + isc_result_totext(eresult)); + + if (l->retries > 1 && !l->tcp_mode) { + dig_query_t *newq = NULL; + + /* + * For UDP, insert a copy of the current query just + * after itself in the list, and start it to retry the + * request. + */ + newq = new_query(l, query->servname, query->userarg); + ISC_LIST_INSERTAFTER(l->q, query, newq, link); + if (l->current_query == query) { + query_detach(&l->current_query); + } + if (l->current_query == NULL) { + l->retries--; + debug("making new UDP request, %d tries left", + l->retries); + start_udp(newq); + } + if (check_if_queries_done(l, query)) { + goto cancel_lookup; + } + + goto detach_query; + } else if (l->retries > 1 && l->tcp_mode) { + /* + * For TCP, we have to requeue the whole lookup, see + * the comments above the start_tcp() function. + */ + l->retries--; + debug("making new TCP request, %d tries left", + l->retries); + requeue_lookup(l, true); + + if (keep != NULL) { + isc_nmhandle_detach(&keep); + } + + goto cancel_lookup; + } else { + dig_query_t *next = ISC_LIST_NEXT(query, link); + + /* + * No retries left, go to the next query, if there is + * one. + */ + if (next != NULL) { + if (l->current_query == query) { + query_detach(&l->current_query); + } + if (l->current_query == NULL) { + debug("starting next query %p", next); + if (l->tcp_mode) { + start_tcp(next); + } else { + start_udp(next); + } + } + if (check_if_queries_done(l, query)) { + goto cancel_lookup; + } + + goto detach_query; + } + + /* + * Otherwise, print the cmdline and an error message, + * and cancel the lookup. + */ + printf("%s", l->cmdline); + dighost_error("no servers could be reached\n"); + + if (exitcode < 9) { + exitcode = 9; + } + + if (keep != NULL) { + isc_nmhandle_detach(&keep); + } + + goto cancel_lookup; + } + } + + isc_buffer_init(&b, region->base, region->length); + isc_buffer_add(&b, region->length); + + peer = isc_nmhandle_peeraddr(handle); + + result = dns_message_peekheader(&b, &id, &msgflags); + if (result != ISC_R_SUCCESS || l->sendmsg->id != id) { + match = false; + if (l->tcp_mode) { + bool fail = true; + if (result == ISC_R_SUCCESS) { + if ((!query->first_soa_rcvd || query->warn_id)) + { + dighost_warning("%s: ID mismatch: " + "expected ID %u, got " + "%u", + query->first_soa_rcvd + ? "WARNING" + : "ERROR", + l->sendmsg->id, id); + } + if (query->first_soa_rcvd) { + fail = false; + } + query->warn_id = false; + } else { + dighost_warning("ERROR: short (< header size) " + "message"); + } + if (fail) { + goto cancel_lookup; + } + match = true; + } else if (result == ISC_R_SUCCESS) { + dighost_warning("Warning: ID mismatch: expected ID %u," + " got %u", + l->sendmsg->id, id); + } else { + dighost_warning("Warning: short (< header size) " + "message received"); + } + } + + if (result == ISC_R_SUCCESS && (msgflags & DNS_MESSAGEFLAG_QR) == 0) { + dighost_warning("Warning: query response not set"); + } + + if (!match) { + /* + * We are still attached to query and the query->readhandle is + * also attached + */ + isc_refcount_increment0(&recvcount); + debug("recvcount=%" PRIuFAST32, + isc_refcount_current(&recvcount)); + isc_nm_read(handle, recv_done, query); + goto keep_query; + } + + dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); + + if (tsigkey != NULL) { + if (l->querysig == NULL) { + debug("getting initial querysig"); + result = dns_message_getquerytsig(l->sendmsg, mctx, + &l->querysig); + check_result(result, "dns_message_getquerytsig"); + } + result = dns_message_setquerytsig(msg, l->querysig); + check_result(result, "dns_message_setquerytsig"); + result = dns_message_settsigkey(msg, tsigkey); + check_result(result, "dns_message_settsigkey"); + msg->tsigctx = l->tsigctx; + l->tsigctx = NULL; + if (l->msgcounter != 0) { + msg->tcp_continuation = 1; + } + l->msgcounter++; + } + + debug("before parse starts"); + parseflags = l->dns64prefix ? 0 : DNS_MESSAGEPARSE_PRESERVEORDER; + if (l->besteffort) { + parseflags |= DNS_MESSAGEPARSE_BESTEFFORT; + parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION; + } + + result = dns_message_parse(msg, &b, parseflags); + if (result == DNS_R_RECOVERABLE) { + dighost_warning("Warning: Message parser reports malformed " + "message packet."); + } else if (result != ISC_R_SUCCESS) { + if (!yaml) { + printf(";; Got bad packet: %s\n", + isc_result_totext(result)); + hex_dump(&b); + } + goto cancel_lookup; + } + + if (msg->opcode != l->opcode) { + char expect[20] = { 0 }, got[20] = { 0 }; + + isc_buffer_init(&b, &expect, sizeof(expect)); + result = dns_opcode_totext(l->opcode, &b); + check_result(result, "dns_opcode_totext"); + + isc_buffer_init(&b, &got, sizeof(got)); + result = dns_opcode_totext(msg->opcode, &b); + check_result(result, "dns_opcode_totext"); + + dighost_warning("Warning: Opcode mismatch: expected %s, got %s", + expect, got); + + isc_refcount_increment0(&recvcount); + debug("recvcount=%" PRIuFAST32, + isc_refcount_current(&recvcount)); + isc_nm_read(handle, recv_done, query); + goto keep_query; + } + + if (msg->counts[DNS_SECTION_QUESTION] != 0) { + match = true; + for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); + result == ISC_R_SUCCESS && match; + result = dns_message_nextname(msg, DNS_SECTION_QUESTION)) + { + dns_name_t *name = NULL; + dns_rdataset_t *rdataset; + + dns_message_currentname(msg, DNS_SECTION_QUESTION, + &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (l->rdtype != rdataset->type || + l->rdclass != rdataset->rdclass || + !dns_name_equal(l->name, name)) + { + char namestr[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char classbuf[DNS_RDATACLASS_FORMATSIZE]; + dns_name_format(name, namestr, + sizeof(namestr)); + dns_rdatatype_format(rdataset->type, + typebuf, + sizeof(typebuf)); + dns_rdataclass_format(rdataset->rdclass, + classbuf, + sizeof(classbuf)); + dighost_warning(";; Question section " + "mismatch: got " + "%s/%s/%s", + namestr, typebuf, + classbuf); + match = false; + } + } + } + + if (!match) { + if (l->tcp_mode) { + goto cancel_lookup; + } + + /* + * We are still attached to query and the + * query->readhandle is also attached + */ + isc_refcount_increment0(&recvcount); + debug("recvcount=%" PRIuFAST32, + isc_refcount_current(&recvcount)); + isc_nm_read(handle, recv_done, query); + goto keep_query; + } + } + + if (msg->rcode == dns_rcode_badvers && msg->opt != NULL && + (newedns = ednsvers(msg->opt)) < l->edns && l->ednsneg) + { + /* + * Add minimum EDNS version required checks here if needed. + */ + dighost_comments(l, "BADVERS, retrying with EDNS version %u.", + (unsigned int)newedns); + l->edns = newedns; + n = requeue_lookup(l, true); + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + goto cancel_lookup; + } + + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 && !l->ignore && + !l->tcp_mode) + { + if (l->cookie == NULL && l->sendcookie && msg->opt != NULL) { + process_opt(l, msg); + } + dighost_comments(l, "Truncated, retrying in TCP mode."); + n = requeue_lookup(l, true); + n->tcp_mode = true; + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + goto cancel_lookup; + } + + if (msg->rcode == dns_rcode_badcookie && !l->tcp_mode && + l->sendcookie && l->badcookie) + { + process_opt(l, msg); + if (msg->cc_ok) { + if (l->showbadcookie) { + dighost_printmessage(query, &b, msg, true); + dighost_received(isc_buffer_usedlength(&b), + &peer, query); + } + dighost_comments(l, "BADCOOKIE, retrying%s.", + l->seenbadcookie ? " in TCP mode" + : ""); + n = requeue_lookup(l, true); + if (l->seenbadcookie) { + n->tcp_mode = true; + } + n->seenbadcookie = true; + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + goto cancel_lookup; + } + done_process_opt = true; + } + + if ((msg->rcode == dns_rcode_servfail && !l->servfail_stops) || + (check_ra && (msg->flags & DNS_MESSAGEFLAG_RA) == 0 && l->recurse)) + { + dig_query_t *next = ISC_LIST_NEXT(query, link); + if (l->current_query == query) { + query_detach(&l->current_query); + } + if (next != NULL) { + debug("sending query %p", next); + if (l->tcp_mode) { + start_tcp(next); + } else { + start_udp(next); + } + dighost_comments(l, + "Got %s from %s, trying next " + "server", + msg->rcode == dns_rcode_servfail + ? "SERVFAIL reply" + : "recursion not available", + query->servname); + if (check_if_queries_done(l, query)) { + goto cancel_lookup; + } + + goto detach_query; + } + } + + if (tsigkey != NULL) { + result = dns_tsig_verify(&b, msg, NULL, NULL); + if (result != ISC_R_SUCCESS) { + dighost_warning("Couldn't verify signature: %s", + isc_result_totext(result)); + validated = false; + } + l->tsigctx = msg->tsigctx; + msg->tsigctx = NULL; + if (l->querysig != NULL) { + debug("freeing querysig buffer %p", l->querysig); + isc_buffer_free(&l->querysig); + } + result = dns_message_getquerytsig(msg, mctx, &l->querysig); + check_result(result, "dns_message_getquerytsig"); + } + + extrabytes = isc_buffer_remaininglength(&b); + + debug("after parse"); + if (l->doing_xfr && l->xfr_q == NULL) { + l->xfr_q = query; + /* + * Once we are in the XFR message, increase + * the timeout to much longer, so brief network + * outages won't cause the XFR to abort + */ + if (timeout != INT_MAX && query->timer != NULL) { + unsigned int local_timeout; + + if (timeout == 0) { + if (l->tcp_mode) { + local_timeout = TCP_TIMEOUT * 4000; + } else { + local_timeout = UDP_TIMEOUT * 4000; + } + } else { + if (timeout < (INT_MAX / 4)) { + local_timeout = timeout * 4000; + } else { + local_timeout = INT_MAX; + } + } + + debug("have local timeout of %d", local_timeout); + isc_nmhandle_settimeout(query->handle, local_timeout); + } + } + + if (!done_process_opt) { + if (l->cookie != NULL) { + if (msg->opt == NULL) { + dighost_warning("expected opt record in " + "response"); + } else { + process_opt(l, msg); + } + } else if (l->sendcookie && msg->opt != NULL) { + process_opt(l, msg); + } + } + + if (!l->doing_xfr || l->xfr_q == query) { + if (msg->rcode == dns_rcode_nxdomain && + (l->origin != NULL || l->need_search)) + { + if (!next_origin(l) || showsearch) { + dighost_printmessage(query, &b, msg, true); + dighost_received(isc_buffer_usedlength(&b), + &peer, query); + } + } else if (!l->trace && !l->ns_search_only) { + dighost_printmessage(query, &b, msg, true); + } else if (l->trace) { + int nl = 0; + int count = msg->counts[DNS_SECTION_ANSWER]; + + debug("in TRACE code"); + if (!l->ns_search_only) { + dighost_printmessage(query, &b, msg, true); + } + + l->rdtype = l->qrdtype; + if (l->trace_root || (l->ns_search_only && count > 0)) { + if (!l->trace_root) { + l->rdtype = dns_rdatatype_soa; + } + nl = followup_lookup(msg, query, + DNS_SECTION_ANSWER); + l->trace_root = false; + } else if (count == 0) { + nl = followup_lookup(msg, query, + DNS_SECTION_AUTHORITY); + } + if (nl == 0) { + docancel = true; + } + } else { + debug("in NSSEARCH code"); + + if (l->trace_root) { + /* + * This is the initial NS query. + */ + int nl; + + l->rdtype = dns_rdatatype_soa; + nl = followup_lookup(msg, query, + DNS_SECTION_ANSWER); + if (nl == 0) { + docancel = true; + } + l->trace_root = false; + usesearch = false; + } else { + /* + * This is a query in the followup lookup + */ + dighost_printmessage(query, &b, msg, true); + + docancel = check_if_queries_done(l, query); + } + } + } + + if (l->pending) { + debug("still pending."); + } + + if (l->doing_xfr) { + if (query != l->xfr_q) { + goto detach_query; + } + if (!docancel) { + docancel = check_for_more_data(l, query, msg, &peer, + region->length); + } + if (docancel) { + goto cancel_lookup; + } + /* + * check_for_more_data() will detach from query->readhandle + * and query on its own, as it needs to reuse the query and + * reattach to the readhandle in launch_next_query(). + */ + goto keep_query; + } else { + if (msg->rcode == dns_rcode_noerror || l->origin == NULL) { + dighost_received(isc_buffer_usedlength(&b), &peer, + query); + } + + if (!l->ns_search_only) { + l->pending = false; + } + if (!l->ns_search_only || l->trace_root || docancel) { + goto cancel_lookup; + } + goto next_lookup; + } +cancel_lookup: + docancel = true; +next_lookup: + donext = true; +detach_query: + isc_nmhandle_detach(&query->readhandle); + query_detach(&query); + if (docancel) { + cancel_lookup(l); + } +keep_query: + if (msg != NULL) { + dns_message_detach(&msg); + } + lookup_detach(&l); + if (donext) { + clear_current_lookup(); + } + UNLOCK_LOOKUP; +} + +/*% + * Turn a name into an address, using system-supplied routines. This is + * used in looking up server names, etc... and needs to use system-supplied + * routines, since they may be using a non-DNS system for these lookups. + */ +isc_result_t +get_address(char *host, in_port_t myport, isc_sockaddr_t *sockaddr) { + int count; + isc_result_t result; + bool is_running; + + is_running = isc_app_isrunning(); + if (is_running) { + isc_app_block(); + } + result = bind9_getaddresses(host, myport, sockaddr, 1, &count); + if (is_running) { + isc_app_unblock(); + } + if (result != ISC_R_SUCCESS) { + return (result); + } + + INSIST(count == 1); + + return (ISC_R_SUCCESS); +} + +int +getaddresses(dig_lookup_t *lookup, const char *host, isc_result_t *resultp) { + isc_result_t result; + isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES]; + isc_netaddr_t netaddr; + int count, i; + dig_server_t *srv; + char tmp[ISC_NETADDR_FORMATSIZE]; + + result = bind9_getaddresses(host, 0, sockaddrs, DIG_MAX_ADDRESSES, + &count); + if (resultp != NULL) { + *resultp = result; + } + if (result != ISC_R_SUCCESS) { + if (resultp == NULL) { + fatal("couldn't get address for '%s': %s", host, + isc_result_totext(result)); + } + return (0); + } + + for (i = 0; i < count; i++) { + isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]); + isc_netaddr_format(&netaddr, tmp, sizeof(tmp)); + srv = make_server(tmp, host); + ISC_LIST_APPEND(lookup->my_server_list, srv, link); + } + + return (count); +} + +/*% + * Initiate either a TCP or UDP lookup + */ +void +do_lookup(dig_lookup_t *lookup) { + dig_query_t *query; + + REQUIRE(lookup != NULL); + + debug("do_lookup()"); + lookup->pending = true; + query = ISC_LIST_HEAD(lookup->q); + if (query != NULL) { + REQUIRE(DIG_VALID_QUERY(query)); + if (lookup->tcp_mode) { + start_tcp(query); + } else { + start_udp(query); + } + } +} + +/*% + * Start everything in action upon task startup. + */ +void +onrun_callback(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + isc_event_free(&event); + LOCK_LOOKUP; + start_lookup(); + UNLOCK_LOOKUP; +} + +/*% + * Make everything on the lookup queue go away. Mainly used by the + * SIGINT handler. + */ +void +cancel_all(void) { + dig_lookup_t *l, *n; + dig_query_t *q, *nq; + + debug("cancel_all()"); + + LOCK_LOOKUP; + if (free_now) { + UNLOCK_LOOKUP; + return; + } + atomic_store(&cancel_now, true); + while (current_lookup != NULL) { + for (q = ISC_LIST_HEAD(current_lookup->q); q != NULL; q = nq) { + nq = ISC_LIST_NEXT(q, link); + debug("canceling pending query %p, belonging to %p", q, + current_lookup); + q->canceled = true; + if (q->readhandle != NULL && + !isc_nm_is_http_handle(q->readhandle)) + { + isc_nm_cancelread(q->readhandle); + } + query_detach(&q); + } + + /* + * current_lookup could have been detached via query_detach(). + */ + if (current_lookup != NULL) { + lookup_detach(¤t_lookup); + } + } + l = ISC_LIST_HEAD(lookup_list); + while (l != NULL) { + n = ISC_LIST_NEXT(l, link); + ISC_LIST_DEQUEUE(lookup_list, l, link); + lookup_detach(&l); + l = n; + } + UNLOCK_LOOKUP; +} + +void +cleanup_openssl_refs(void) { + if (tsigkey != NULL) { + debug("freeing TSIG key %p", tsigkey); + dns_tsigkey_detach(&tsigkey); + } + + if (sig0key != NULL) { + debug("freeing SIG(0) key %p", sig0key); + dst_key_free(&sig0key); + } + + if (is_dst_up) { + debug("destroy DST lib"); + dst_lib_destroy(); + is_dst_up = false; + } +} + +/*% + * Destroy all of the libs we are using, and get everything ready for a + * clean shutdown. + */ +void +destroy_libs(void) { + if (keep != NULL) { + isc_nmhandle_detach(&keep); + } + debug("destroy_libs()"); + if (global_task != NULL) { + debug("freeing task"); + isc_task_detach(&global_task); + } + + isc_managers_destroy(&netmgr, &taskmgr, NULL); + + LOCK_LOOKUP; + isc_refcount_destroy(&recvcount); + isc_refcount_destroy(&sendcount); + + INSIST(ISC_LIST_HEAD(lookup_list) == NULL); + INSIST(current_lookup == NULL); + INSIST(!free_now); + + free_now = true; + + flush_server_list(); + + clear_searchlist(); + + cleanup_openssl_refs(); + + if (namebuf != NULL) { + debug("freeing key %p", tsigkey); + isc_buffer_free(&namebuf); + } + + UNLOCK_LOOKUP; + isc_mutex_destroy(&lookup_lock); + + debug("Removing log context"); + isc_log_destroy(&lctx); + + debug("Destroy memory"); + if (memdebugging != 0) { + isc_mem_stats(mctx, stderr); + } + if (mctx != NULL) { + isc_mem_destroy(&mctx); + } +} + +#ifdef HAVE_LIBIDN2 +static isc_result_t +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org) { + char src[MXNAME], *dst = NULL; + size_t srclen, dstlen; + isc_result_t result = ISC_R_SUCCESS; + + /* + * Copy name from 'buffer' to 'src' and terminate it with NULL. + */ + srclen = isc_buffer_usedlength(buffer) - used_org; + if (srclen >= sizeof(src)) { + warn("Input name too long to perform IDN conversion"); + goto cleanup; + } + memmove(src, (char *)isc_buffer_base(buffer) + used_org, srclen); + src[srclen] = '\0'; + + systemlocale(LC_ALL); + + /* + * Convert 'src' to the current locale's character encoding. + */ + idn_ace_to_locale(src, &dst); + + resetlocale(LC_ALL); + + /* + * Check whether the converted name will fit back into 'buffer'. + */ + dstlen = strlen(dst); + if (isc_buffer_length(buffer) < used_org + dstlen) { + result = ISC_R_NOSPACE; + goto cleanup; + } + + /* + * Put the converted name back into 'buffer'. + */ + isc_buffer_subtract(buffer, srclen); + memmove(isc_buffer_used(buffer), dst, dstlen); + isc_buffer_add(buffer, dstlen); + + /* + * Clean up. + */ +cleanup: + if (dst != NULL) { + idn2_free(dst); + } + + return (result); +} + +/*% + * Convert 'src', which is a string using the current locale's character + * encoding, into an ACE string suitable for use in the DNS, storing the + * conversion result in 'dst', which is 'dstlen' bytes large. + * + * 'dst' MUST be large enough to hold any valid domain name. + */ +static void +idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { + const char *final_src; + char *ascii_src; + int res; + + systemlocale(LC_ALL); + + /* + * We trust libidn2 to return an error if 'src' is too large to be a + * valid domain name. + */ + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_NONTRANSITIONAL); + if (res == IDN2_DISALLOWED) { + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_TRANSITIONAL); + } + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", + src, idn2_strerror(res)); + } + + /* + * idn2_to_ascii_lz() normalizes all strings to lower case, but we + * generally don't want to lowercase all input strings; make sure to + * return the original case if the two strings differ only in case. + */ + final_src = (strcasecmp(src, ascii_src) == 0 ? src : ascii_src); + + (void)strlcpy(dst, final_src, dstlen); + + idn2_free(ascii_src); + + resetlocale(LC_ALL); +} + +/*% + * Convert 'src', which is an ACE string suitable for use in the DNS, into a + * string using the current locale's character encoding, storing the conversion + * result in 'dst'. + * + * The caller MUST subsequently release 'dst' using idn2_free(). + */ +static void +idn_ace_to_locale(const char *src, char **dst) { + char *local_src, *utf8_src; + int res; + + systemlocale(LC_ALL); + + /* + * We need to: + * + * 1) check whether 'src' is a valid IDNA2008 name, + * 2) if it is, output it in the current locale's character encoding. + * + * Unlike idn2_to_ascii_*(), idn2_to_unicode_*() functions are unable + * to perform IDNA2008 validity checks. Thus, we need to decode any + * Punycode in 'src', check if the resulting name is a valid IDNA2008 + * name, and only once we ensure it is, output that name in the current + * locale's character encoding. + * + * We could just use idn2_to_unicode_8zlz() + idn2_to_ascii_lz(), but + * then we would not be able to universally tell invalid names and + * character encoding errors apart (if the current locale uses ASCII + * for character encoding, the former function would fail even for a + * valid IDNA2008 name, as long as it contained any non-ASCII + * character). Thus, we need to take a longer route. + * + * First, convert 'src' to UTF-8, ignoring the current locale. + */ + res = idn2_to_unicode_8z8z(src, &utf8_src, 0); + if (res != IDN2_OK) { + fatal("Bad ACE string '%s' (%s), use +noidnout", src, + idn2_strerror(res)); + } + + /* + * Then, check whether decoded 'src' is a valid IDNA2008 name + * and if disallowed character is found, fallback to IDNA2003. + */ + res = idn2_to_ascii_8z(utf8_src, NULL, IDN2_NONTRANSITIONAL); + if (res == IDN2_DISALLOWED) { + res = idn2_to_ascii_8z(utf8_src, NULL, IDN2_TRANSITIONAL); + } + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout", + src, idn2_strerror(res)); + } + + /* + * Finally, try converting the decoded 'src' into the current locale's + * character encoding. + */ + res = idn2_to_unicode_8zlz(utf8_src, &local_src, 0); + if (res != IDN2_OK) { + static bool warned = false; + + res = idn2_to_ascii_8z(utf8_src, &local_src, 0); + if (res != IDN2_OK) { + fatal("Cannot represent '%s' " + "in the current locale nor ascii (%s), " + "use +noidnout or a different locale", + src, idn2_strerror(res)); + } else if (!warned) { + fprintf(stderr, + ";; Warning: cannot represent '%s' " + "in the current locale", + local_src); + warned = true; + } + } + + /* + * Free the interim conversion result. + */ + idn2_free(utf8_src); + + *dst = local_src; + + resetlocale(LC_ALL); +} +#endif /* HAVE_LIBIDN2 */ + +void +dig_idnsetup(dig_lookup_t *lookup, bool active) { +#ifdef HAVE_LIBIDN2 + isc_result_t result; + result = dns_name_settotextfilter( + (active && lookup->idnout) ? idn_output_filter : NULL); + check_result(result, "dns_name_settotextfilter"); +#else + UNUSED(lookup); + UNUSED(active); + return; +#endif /* HAVE_LIBIDN2 */ +} + +bool +dig_lookup_is_tls(const dig_lookup_t *lookup) { + if (lookup->tls_mode || (lookup->tls_ca_set && !lookup->https_mode)) { + return (true); + } + + return (false); +} diff --git a/bin/dig/dighost.h b/bin/dig/dighost.h new file mode 100644 index 0000000..227c315 --- /dev/null +++ b/bin/dig/dighost.h @@ -0,0 +1,466 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef __APPLE__ +#include +#endif /* ifdef __APPLE__ */ + +#define MXSERV 20 +#define MXNAME (DNS_NAME_MAXTEXT + 1) +#define MXRD 32 +/*% Buffer Size */ +#define BUFSIZE 512 +#define COMMSIZE 0xffff +#ifndef RESOLV_CONF +/*% location of resolve.conf */ +#define RESOLV_CONF "/etc/resolv.conf" +#endif /* ifndef RESOLV_CONF */ +/*% output buffer */ +#define OUTPUTBUF 32767 +/*% Max RR Limit */ +#define MAXRRLIMIT 0xffffffff +#define MAXTIMEOUT 0xffff +/*% Max number of tries */ +#define MAXTRIES 0xffffffff +/*% Max number of dots */ +#define MAXNDOTS 0xffff +/*% Max number of ports */ +#define MAXPORT 0xffff +/*% Max serial number */ +#define MAXSERIAL 0xffffffff +/*% Max query ID */ +#define MAXQID 0xffff + +/*% Default TCP Timeout */ +#define TCP_TIMEOUT 10 +/*% Default UDP Timeout */ +#define UDP_TIMEOUT 5 + +#define SERVER_TIMEOUT 1 + +#define LOOKUP_LIMIT 64 + +#define DEFAULT_EDNS_VERSION 0 +#define DEFAULT_EDNS_BUFSIZE 1232 + +#define DEFAULT_HTTPS_QUERY "?dns=" + +/*% + * Lookup_limit is just a limiter, keeping too many lookups from being + * created. It's job is mainly to prevent the program from running away + * in a tight loop of constant lookups. It's value is arbitrary. + */ + +ISC_LANG_BEGINDECLS + +typedef struct dig_lookup dig_lookup_t; +typedef struct dig_query dig_query_t; +typedef struct dig_server dig_server_t; +typedef ISC_LIST(dig_server_t) dig_serverlist_t; +typedef struct dig_searchlist dig_searchlist_t; + +#define DIG_LOOKUP_MAGIC ISC_MAGIC('D', 'i', 'g', 'l') + +#define DIG_VALID_LOOKUP(x) ISC_MAGIC_VALID((x), DIG_LOOKUP_MAGIC) + +#define DIG_QUERY_MAGIC ISC_MAGIC('D', 'i', 'g', 'q') + +#define DIG_VALID_QUERY(x) ISC_MAGIC_VALID((x), DIG_QUERY_MAGIC) + +/*% The dig_lookup structure */ +struct dig_lookup { + unsigned int magic; + isc_refcount_t references; + bool aaonly, adflag, badcookie, besteffort, cdflag, cleared, comments, + dns64prefix, dnssec, doing_xfr, done_as_is, ednsneg, expandaaaa, + expire, fuzzing, header_only, identify, /*%< Append an "on + server " message + */ + identify_previous_line, /*% Prepend a "Nameserver :" + message, with newline and tab */ + idnin, idnout, ignore, multiline, need_search, new_search, + noclass, nocrypto, nottl, ns_search_only, /*%< dig +nssearch, + host -C */ + ns_search_success, nsid, /*% Name Server ID (RFC 5001) */ + onesoa, pending, /*%< Pending a successful answer */ + print_unknown_format, qr, raflag, recurse, section_additional, + section_answer, section_authority, section_question, + seenbadcookie, sendcookie, servfail_stops, + setqid, /*% use a speciied query ID */ + showbadcookie, stats, tcflag, tcp_keepalive, tcp_mode, + tcp_mode_set, tls_mode, /*% connect using TLS */ + trace, /*% dig +trace */ + trace_root, /*% initial query for either +trace or +nssearch */ + ttlunits, use_usec, waiting_connect, zflag; + char textname[MXNAME]; /*% Name we're going to be looking up */ + char cmdline[MXNAME]; + dns_rdatatype_t rdtype; + dns_rdatatype_t qrdtype; + dns_rdataclass_t rdclass; + bool rdtypeset; + bool rdclassset; + char name_space[BUFSIZE]; + char oname_space[BUFSIZE]; + isc_buffer_t namebuf; + isc_buffer_t onamebuf; + isc_buffer_t renderbuf; + char *sendspace; + dns_name_t *name; + isc_interval_t interval; + dns_message_t *sendmsg; + dns_name_t *oname; + ISC_LINK(dig_lookup_t) link; + ISC_LIST(dig_query_t) q; + ISC_LIST(dig_query_t) connecting; + dig_query_t *current_query; + dig_serverlist_t my_server_list; + dig_searchlist_t *origin; + dig_query_t *xfr_q; + uint32_t retries; + int nsfound; + int16_t udpsize; + int16_t edns; + int16_t padding; + uint32_t ixfr_serial; + isc_buffer_t rdatabuf; + char rdatastore[MXNAME]; + dst_context_t *tsigctx; + isc_buffer_t *querysig; + uint32_t msgcounter; + dns_fixedname_t fdomain; + isc_sockaddr_t *ecs_addr; + char *cookie; + dns_ednsopt_t *ednsopts; + unsigned int ednsoptscnt; + unsigned int ednsflags; + dns_opcode_t opcode; + int rrcomments; + uint16_t qid; + struct { + bool http_plain; + bool https_mode; + bool https_get; + char *https_path; + }; + struct { + bool tls_ca_set; + char *tls_ca_file; + bool tls_hostname_set; + char *tls_hostname; + bool tls_cert_file_set; + char *tls_cert_file; + bool tls_key_file_set; + char *tls_key_file; + isc_tlsctx_cache_t *tls_ctx_cache; + }; + isc_stdtime_t fuzztime; +}; + +/*% The dig_query structure */ +struct dig_query { + unsigned int magic; + dig_lookup_t *lookup; + bool started; + bool first_soa_rcvd; + bool second_rr_rcvd; + bool first_repeat_rcvd; + bool warn_id; + bool canceled; + uint32_t first_rr_serial; + uint32_t second_rr_serial; + uint32_t msg_count; + uint32_t rr_count; + bool ixfr_axfr; + char *servname; + char *userarg; + isc_buffer_t sendbuf; + char *recvspace, *tmpsendspace, lengthspace[4]; + isc_refcount_t references; + isc_nmhandle_t *handle; + isc_nmhandle_t *readhandle; + isc_nmhandle_t *sendhandle; + ISC_LINK(dig_query_t) link; + ISC_LINK(dig_query_t) clink; + isc_sockaddr_t sockaddr; + isc_time_t time_sent; + isc_time_t time_recv; + uint64_t byte_count; + isc_timer_t *timer; +}; + +struct dig_server { + char servername[MXNAME]; + char userarg[MXNAME]; + ISC_LINK(dig_server_t) link; +}; + +struct dig_searchlist { + char origin[MXNAME]; + ISC_LINK(dig_searchlist_t) link; +}; + +typedef ISC_LIST(dig_searchlist_t) dig_searchlistlist_t; +typedef ISC_LIST(dig_lookup_t) dig_lookuplist_t; + +/* + * Externals from dighost.c + */ + +extern dig_lookuplist_t lookup_list; +extern dig_serverlist_t server_list; +extern dig_searchlistlist_t search_list; +extern unsigned int extrabytes; + +extern bool check_ra, have_ipv4, have_ipv6, specified_source, usesearch, + showsearch, yaml; +extern in_port_t port; +extern bool port_set; +extern unsigned int timeout; +extern isc_mem_t *mctx; +extern isc_refcount_t sendcount; +extern int ndots; +extern int lookup_counter; +extern int exitcode; +extern isc_sockaddr_t localaddr; +extern char keynametext[MXNAME]; +extern char keyfile[MXNAME]; +extern char keysecret[MXNAME]; +extern const dns_name_t *hmacname; +extern unsigned int digestbits; +extern dns_tsigkey_t *tsigkey; +extern bool validated; +extern isc_taskmgr_t *taskmgr; +extern isc_task_t *global_task; +extern bool free_now; +extern bool debugging, debugtiming, memdebugging; +extern bool keep_open; + +extern char *progname; +extern int tries; +extern int fatalexit; +extern bool verbose; + +/* + * Routines in dighost.c. + */ +isc_result_t +get_address(char *host, in_port_t myport, isc_sockaddr_t *sockaddr); + +int +getaddresses(dig_lookup_t *lookup, const char *host, isc_result_t *resultp); + +isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict); + +noreturn void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +warn(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +noreturn void +digexit(void); + +void +cleanup_openssl_refs(void); + +void +debug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +check_result(isc_result_t result, const char *msg); + +bool +setup_lookup(dig_lookup_t *lookup); + +void +destroy_lookup(dig_lookup_t *lookup); + +void +do_lookup(dig_lookup_t *lookup); + +void +start_lookup(void); + +void +onrun_callback(isc_task_t *task, isc_event_t *event); + +int +dhmain(int argc, char **argv); + +void +setup_libs(void); + +void +setup_system(bool ipv4only, bool ipv6only); + +isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc); + +isc_result_t +parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc); + +isc_result_t +parse_netprefix(isc_sockaddr_t **sap, const char *value); + +void +parse_hmac(const char *hmacstr); + +dig_lookup_t * +requeue_lookup(dig_lookup_t *lookold, bool servers); + +dig_lookup_t * +make_empty_lookup(void); + +dig_lookup_t * +clone_lookup(dig_lookup_t *lookold, bool servers); + +dig_server_t * +make_server(const char *servname, const char *userarg); + +void +flush_server_list(void); + +void +set_nameserver(char *opt); + +void +clone_server_list(dig_serverlist_t src, dig_serverlist_t *dest); + +void +cancel_all(void); + +void +destroy_libs(void); + +void +set_search_domain(char *domain); + +/* + * Routines to be defined in dig.c, host.c, and nslookup.c. and + * then assigned to the appropriate function pointer + */ +extern isc_result_t (*dighost_printmessage)(dig_query_t *query, + const isc_buffer_t *msgbuf, + dns_message_t *msg, bool headers); + +/* + * Print an error message in the appropriate format. + */ +extern void (*dighost_error)(const char *format, ...); + +/* + * Print a warning message in the appropriate format. + */ +extern void (*dighost_warning)(const char *format, ...); + +/* + * Print a comment in the appropriate format. + */ +extern void (*dighost_comments)(dig_lookup_t *lookup, const char *format, ...); + +/*%< + * Print the final result of the lookup. + */ + +extern void (*dighost_received)(unsigned int bytes, isc_sockaddr_t *from, + dig_query_t *query); +/*%< + * Print a message about where and when the response + * was received from, like the final comment in the + * output of "dig". + */ + +extern void (*dighost_trying)(char *frm, dig_lookup_t *lookup); + +extern void (*dighost_shutdown)(void); + +extern void (*dighost_pre_exit_hook)(void); + +void +save_opt(dig_lookup_t *lookup, char *code, char *value); + +void +setup_file_key(void); +void +setup_text_key(void); + +/* + * Routines exported from dig.c for use by dig for iOS + */ + +/*% + * Call once only to set up libraries, parse global + * parameters and initial command line query parameters + */ +void +dig_setup(int argc, char **argv); + +/*% + * Call to supply new parameters for the next lookup + */ +void +dig_query_setup(bool, bool, int argc, char **argv); + +/*% + * set the main application event cycle running + */ +void +dig_startup(void); + +/*% + * Initiates the next lookup cycle + */ +void +dig_query_start(void); + +/*% + * Activate/deactivate IDN filtering of output. + */ +void +dig_idnsetup(dig_lookup_t *lookup, bool active); + +/*% + * Cleans up the application + */ +void +dig_shutdown(void); + +bool +dig_lookup_is_tls(const dig_lookup_t *lookup); + +ISC_LANG_ENDDECLS diff --git a/bin/dig/host.c b/bin/dig/host.c new file mode 100644 index 0000000..011587a --- /dev/null +++ b/bin/dig/host.c @@ -0,0 +1,931 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dighost.h" + +static bool short_form = true, listed_server = false; +static bool default_lookups = true; +static int seen_error = -1; +static bool list_addresses = true; +static bool list_almost_all = false; +static dns_rdatatype_t list_type = dns_rdatatype_a; +static bool printed_server = false; +static bool ipv4only = false, ipv6only = false; + +static const char *opcodetext[] = { "QUERY", "IQUERY", "STATUS", + "RESERVED3", "NOTIFY", "UPDATE", + "RESERVED6", "RESERVED7", "RESERVED8", + "RESERVED9", "RESERVED10", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15" }; + +static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL", + "NXDOMAIN", "NOTIMP", "REFUSED", + "YXDOMAIN", "YXRRSET", "NXRRSET", + "NOTAUTH", "NOTZONE", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15", "BADVERS" }; + +struct rtype { + unsigned int type; + const char *text; +}; + +struct rtype rtypes[] = { { 1, "has address" }, + { 2, "name server" }, + { 5, "is an alias for" }, + { 11, "has well known services" }, + { 12, "domain name pointer" }, + { 13, "host information" }, + { 15, "mail is handled by" }, + { 16, "descriptive text" }, + { 19, "x25 address" }, + { 20, "ISDN address" }, + { 24, "has signature" }, + { 25, "has key" }, + { 28, "has IPv6 address" }, + { 29, "location" }, + { 0, NULL } }; + +static char * +rcode_totext(dns_rcode_t rcode) { + static char buf[sizeof("?65535")]; + union { + const char *consttext; + char *deconsttext; + } totext; + + if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { + snprintf(buf, sizeof(buf), "?%u", rcode); + totext.deconsttext = buf; + } else { + totext.consttext = rcodetext[rcode]; + } + return (totext.deconsttext); +} + +noreturn static void +show_usage(void); + +static void +show_usage(void) { + fprintf(stderr, + "Usage: host [-aCdilrTvVw] [-c class] [-N ndots] [-t type] [-W " + "time]\n" + " [-R number] [-m flag] [-p port] hostname " + "[server]\n" + " -a is equivalent to -v -t ANY\n" + " -A is like -a but omits RRSIG, NSEC, NSEC3\n" + " -c specifies query class for non-IN data\n" + " -C compares SOA records on authoritative nameservers\n" + " -d is equivalent to -v\n" + " -l lists all hosts in a domain, using AXFR\n" + " -m set memory debugging flag (trace|record|usage)\n" + " -N changes the number of dots allowed before root " + "lookup " + "is done\n" + " -p specifies the port on the server to query\n" + " -r disables recursive processing\n" + " -R specifies number of retries for UDP packets\n" + " -s a SERVFAIL response should stop query\n" + " -t specifies the query type\n" + " -T enables TCP/IP mode\n" + " -U enables UDP mode\n" + " -v enables verbose output\n" + " -V print version number and exit\n" + " -w specifies to wait forever for a reply\n" + " -W specifies how long to wait for a reply\n" + " -4 use IPv4 query transport only\n" + " -6 use IPv6 query transport only\n"); + exit(1); +} + +static void +host_shutdown(void) { + (void)isc_app_shutdown(); +} + +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + isc_time_t now; + int diff; + + if (!short_form) { + char fromtext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(from, fromtext, sizeof(fromtext)); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&now); + } else { + TIME_NOW(&now); + } + diff = (int)isc_time_microdiff(&now, &query->time_sent); + printf("Received %u bytes from %s in %d ms\n", bytes, fromtext, + diff / 1000); + } +} + +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(lookup); + + if (!short_form) { + printf("Trying \"%s\"\n", frm); + } +} + +static void +say_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata, + dig_query_t *query) { + isc_buffer_t *b = NULL; + char namestr[DNS_NAME_FORMATSIZE]; + isc_region_t r; + isc_result_t result; + unsigned int bufsize = BUFSIZ; + + dns_name_format(name, namestr, sizeof(namestr)); +retry: + isc_buffer_allocate(mctx, &b, bufsize); + result = dns_rdata_totext(rdata, NULL, b); + if (result == ISC_R_NOSPACE) { + isc_buffer_free(&b); + bufsize *= 2; + goto retry; + } + check_result(result, "dns_rdata_totext"); + isc_buffer_usedregion(b, &r); + if (query->lookup->identify_previous_line) { + printf("Nameserver %s:\n\t", query->servname); + } + printf("%s %s %.*s", namestr, msg, (int)r.length, (char *)r.base); + if (query->lookup->identify) { + printf(" on server %s", query->servname); + } + printf("\n"); + isc_buffer_free(&b); +} + +static isc_result_t +printsection(dns_message_t *msg, dns_section_t sectionid, + const char *section_name, bool headers, dig_query_t *query) { + dns_name_t *name, *print_name; + dns_rdataset_t *rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_buffer_t target; + isc_result_t result, loopresult; + isc_region_t r; + dns_name_t empty_name; + char tbuf[4096] = { 0 }; + bool first; + bool no_rdata = (sectionid == DNS_SECTION_QUESTION); + + if (headers) { + printf(";; %s SECTION:\n", section_name); + } + + dns_name_init(&empty_name, NULL); + + result = dns_message_firstname(msg, sectionid); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + + for (;;) { + name = NULL; + dns_message_currentname(msg, sectionid, &name); + + isc_buffer_init(&target, tbuf, sizeof(tbuf)); + first = true; + print_name = name; + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (query->lookup->rdtype == dns_rdatatype_axfr && + !((!list_addresses && + (list_type == dns_rdatatype_any || + rdataset->type == list_type)) || + (list_addresses && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa || + rdataset->type == dns_rdatatype_ns || + rdataset->type == dns_rdatatype_ptr)))) + { + continue; + } + if (list_almost_all && + (rdataset->type == dns_rdatatype_rrsig || + rdataset->type == dns_rdatatype_nsec || + rdataset->type == dns_rdatatype_nsec3)) + { + continue; + } + if (!short_form) { + result = dns_rdataset_totext(rdataset, + print_name, false, + no_rdata, &target); + if (result != ISC_R_SUCCESS) { + return (result); + } +#ifdef USEINITALWS + if (first) { + print_name = &empty_name; + first = false; + } +#else /* ifdef USEINITALWS */ + UNUSED(first); /* Shut up compiler. */ +#endif /* ifdef USEINITALWS */ + } else { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + struct rtype *t; + const char *rtt; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char typebuf2[DNS_RDATATYPE_FORMATSIZE + + 20]; + dns_rdataset_current(rdataset, &rdata); + + for (t = rtypes; t->text != NULL; t++) { + if (t->type == rdata.type) { + rtt = t->text; + goto found; + } + } + + dns_rdatatype_format(rdata.type, + typebuf, + sizeof(typebuf)); + snprintf(typebuf2, sizeof(typebuf2), + "has %s record", typebuf); + rtt = typebuf2; + found: + say_message(print_name, rtt, &rdata, + query); + dns_rdata_reset(&rdata); + loopresult = + dns_rdataset_next(rdataset); + } + } + } + if (!short_form) { + isc_buffer_usedregion(&target, &r); + if (no_rdata) { + printf(";%.*s", (int)r.length, (char *)r.base); + } else { + printf("%.*s", (int)r.length, (char *)r.base); + } + } + + result = dns_message_nextname(msg, sectionid); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +printrdata(dns_message_t *msg, dns_rdataset_t *rdataset, + const dns_name_t *owner, const char *set_name, bool headers) { + isc_buffer_t target; + isc_result_t result; + isc_region_t r; + char tbuf[4096]; + + UNUSED(msg); + if (headers) { + printf(";; %s SECTION:\n", set_name); + } + + isc_buffer_init(&target, tbuf, sizeof(tbuf)); + + result = dns_rdataset_totext(rdataset, owner, false, false, &target); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_usedregion(&target, &r); + printf("%.*s", (int)r.length, (char *)r.base); + + return (ISC_R_SUCCESS); +} + +static void +chase_cnamechain(dns_message_t *msg, dns_name_t *qname) { + isc_result_t result; + dns_rdataset_t *rdataset; + dns_rdata_cname_t cname; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i = msg->counts[DNS_SECTION_ANSWER]; + + while (i-- > 0) { + rdataset = NULL; + result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname, + dns_rdatatype_cname, 0, NULL, + &rdataset); + if (result != ISC_R_SUCCESS) { + return; + } + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_copy(&cname.cname, qname); + dns_rdata_freestruct(&cname); + } +} + +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + bool did_flag = false; + dns_rdataset_t *opt, *tsig = NULL; + const dns_name_t *tsigname; + isc_result_t result = ISC_R_SUCCESS; + int force_error; + + UNUSED(msgbuf); + UNUSED(headers); + + /* + * We get called multiple times. + * Preserve any existing error status. + */ + force_error = (seen_error == 1) ? 1 : 0; + seen_error = 1; + if (listed_server && !printed_server) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + + printf("Using domain server:\n"); + printf("Name: %s\n", query->userarg); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + printf("Address: %s\n", sockstr); + printf("Aliases: \n\n"); + printed_server = true; + } + + if (msg->rcode != 0) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(query->lookup->name, namestr, sizeof(namestr)); + + if (query->lookup->identify_previous_line) { + printf("Nameserver %s:\n\t%s not found: %d(%s)\n", + query->servname, + (msg->rcode != dns_rcode_nxdomain) + ? namestr + : query->lookup->textname, + msg->rcode, rcode_totext(msg->rcode)); + } else { + printf("Host %s not found: %d(%s)\n", + (msg->rcode != dns_rcode_nxdomain) + ? namestr + : query->lookup->textname, + msg->rcode, rcode_totext(msg->rcode)); + } + return (ISC_R_SUCCESS); + } + + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) { + char namestr[DNS_NAME_FORMATSIZE]; + dig_lookup_t *lookup; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Add AAAA and MX lookups. */ + name = dns_fixedname_initname(&fixed); + dns_name_copy(query->lookup->name, name); + chase_cnamechain(msg, name); + dns_name_format(name, namestr, sizeof(namestr)); + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_aaaa; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_mx; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + } + + if (!short_form) { + printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n", + opcodetext[msg->opcode], rcode_totext(msg->rcode), + msg->id); + printf(";; flags: "); + if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) { + printf("qr"); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) { + printf("%saa", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { + printf("%stc", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + printf("%srd", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) { + printf("%sra", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) { + printf("%sad", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) { + printf("%scd", did_flag ? " " : ""); + did_flag = true; + POST(did_flag); + } + printf("; QUERY: %u, ANSWER: %u, " + "AUTHORITY: %u, ADDITIONAL: %u\n", + msg->counts[DNS_SECTION_QUESTION], + msg->counts[DNS_SECTION_ANSWER], + msg->counts[DNS_SECTION_AUTHORITY], + msg->counts[DNS_SECTION_ADDITIONAL]); + opt = dns_message_getopt(msg); + if (opt != NULL) { + printf(";; EDNS: version: %u, udp=%u\n", + (unsigned int)((opt->ttl & 0x00ff0000) >> 16), + (unsigned int)opt->rdclass); + } + tsigname = NULL; + tsig = dns_message_gettsig(msg, &tsigname); + if (tsig != NULL) { + printf(";; PSEUDOSECTIONS: TSIG\n"); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) && !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) { + if (!short_form) { + printf("\n"); + } + result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER", + !short_form, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) && + !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) && + !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_ADDITIONAL, "ADDITIONAL", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if ((tsig != NULL) && !short_form) { + printf("\n"); + result = printrdata(msg, tsig, tsigname, "PSEUDOSECTION TSIG", + true); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!short_form) { + printf("\n"); + } + + if (short_form && !default_lookups && + ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) + { + char namestr[DNS_NAME_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; + dns_name_format(query->lookup->name, namestr, sizeof(namestr)); + dns_rdatatype_format(query->lookup->rdtype, typestr, + sizeof(typestr)); + printf("%s has no %s record\n", namestr, typestr); + } + seen_error = force_error; + return (result); +} + +static const char *optstring = "46aAc:dilnm:p:rst:vVwCDN:R:TUW:"; + +/*% version */ +static void +version(void) { + fprintf(stderr, "host %s\n", PACKAGE_VERSION); +} + +static void +pre_parse_args(int argc, char **argv) { + int c; + + while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) { + switch (c) { + case 'm': + memdebugging = true; + if (strcasecmp("trace", isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } else if (strcasecmp("record", + isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } else if (strcasecmp("usage", + isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + case 'a': + break; + case 'A': + break; + case 'c': + break; + case 'C': + break; + case 'd': + break; + case 'D': + if (debugging) { + debugtiming = true; + } + debugging = true; + break; + case 'i': + break; + case 'l': + break; + case 'n': + break; + case 'N': + break; + case 'p': + break; + case 'r': + break; + case 'R': + break; + case 's': + break; + case 't': + break; + case 'T': + break; + case 'U': + break; + case 'v': + break; + case 'V': + version(); + exit(0); + break; + case 'w': + break; + case 'W': + break; + default: + show_usage(); + } + } + isc_commandline_reset = true; + isc_commandline_index = 1; +} + +static void +parse_args(bool is_batchfile, int argc, char **argv) { + char hostname[MXNAME]; + dig_lookup_t *lookup; + int c; + char store[MXNAME]; + isc_textregion_t tr; + isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + uint32_t serial = 0; + + UNUSED(is_batchfile); + + lookup = make_empty_lookup(); + + lookup->servfail_stops = false; + lookup->besteffort = false; + lookup->comments = false; + short_form = !verbose; + + while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) { + switch (c) { + case 'l': + lookup->tcp_mode = true; + lookup->rdtype = dns_rdatatype_axfr; + lookup->rdtypeset = true; + fatalexit = 3; + break; + case 'v': + case 'd': + short_form = false; + break; + case 'r': + lookup->recurse = false; + break; + case 't': + if (strncasecmp(isc_commandline_argument, "ixfr=", 5) == + 0) + { + rdtype = dns_rdatatype_ixfr; + /* XXXMPA add error checking */ + serial = strtoul(isc_commandline_argument + 5, + NULL, 10); + result = ISC_R_SUCCESS; + } else { + tr.base = isc_commandline_argument; + tr.length = strlen(isc_commandline_argument); + result = dns_rdatatype_fromtext( + &rdtype, (isc_textregion_t *)&tr); + } + + if (result != ISC_R_SUCCESS) { + fatalexit = 2; + fatal("invalid type: %s\n", + isc_commandline_argument); + } + if (!lookup->rdtypeset || + lookup->rdtype != dns_rdatatype_axfr) + { + lookup->rdtype = rdtype; + } + lookup->rdtypeset = true; + if (rdtype == dns_rdatatype_axfr) { + /* -l -t any -v */ + list_type = dns_rdatatype_any; + short_form = false; + lookup->tcp_mode = true; + } else if (rdtype == dns_rdatatype_ixfr) { + lookup->ixfr_serial = serial; + lookup->tcp_mode = true; + list_type = rdtype; + } else if (rdtype == dns_rdatatype_any) { + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = true; + } + } else { + list_type = rdtype; + } + list_addresses = false; + default_lookups = false; + break; + case 'c': + tr.base = isc_commandline_argument; + tr.length = strlen(isc_commandline_argument); + result = dns_rdataclass_fromtext( + &rdclass, (isc_textregion_t *)&tr); + + if (result != ISC_R_SUCCESS) { + fatalexit = 2; + fatal("invalid class: %s\n", + isc_commandline_argument); + } else { + lookup->rdclass = rdclass; + lookup->rdclassset = true; + } + default_lookups = false; + break; + case 'A': + list_almost_all = true; + FALLTHROUGH; + case 'a': + if (!lookup->rdtypeset || + lookup->rdtype != dns_rdatatype_axfr) + { + lookup->rdtype = dns_rdatatype_any; + } + list_type = dns_rdatatype_any; + list_addresses = false; + lookup->rdtypeset = true; + short_form = false; + default_lookups = false; + break; + case 'i': + /* deprecated */ + break; + case 'n': + /* deprecated */ + break; + case 'm': + /* Handled by pre_parse_args(). */ + break; + case 'w': + /* + * The timer routines are coded such that + * timeout==MAXINT doesn't enable the timer + */ + timeout = INT_MAX; + break; + case 'W': + timeout = atoi(isc_commandline_argument); + if (timeout < 1) { + timeout = 1; + } + break; + case 'R': + tries = atoi(isc_commandline_argument) + 1; + if (tries < 2) { + tries = 2; + } + break; + case 'T': + lookup->tcp_mode = true; + lookup->tcp_mode_set = true; + break; + case 'U': + lookup->tcp_mode = false; + lookup->tcp_mode_set = true; + break; + case 'C': + debug("showing all SOAs"); + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + lookup->rdclass = dns_rdataclass_in; + lookup->rdclassset = true; + lookup->ns_search_only = true; + lookup->trace_root = true; + lookup->identify_previous_line = true; + default_lookups = false; + break; + case 'N': + debug("setting NDOTS to %s", isc_commandline_argument); + ndots = atoi(isc_commandline_argument); + break; + case 'D': + /* Handled by pre_parse_args(). */ + break; + case '4': + /* Handled by pre_parse_args(). */ + break; + case '6': + /* Handled by pre_parse_args(). */ + break; + case 's': + lookup->servfail_stops = true; + break; + case 'p': + port = atoi(isc_commandline_argument); + port_set = true; + break; + } + } + + lookup->retries = tries; + + if (isc_commandline_index >= argc) { + show_usage(); + } + + strlcpy(hostname, argv[isc_commandline_index], sizeof(hostname)); + + if (argc > isc_commandline_index + 1) { + set_nameserver(argv[isc_commandline_index + 1]); + debug("server is %s", argv[isc_commandline_index + 1]); + listed_server = true; + } else { + check_ra = true; + } + + lookup->pending = false; + if (get_reverse(store, sizeof(store), hostname, true) == ISC_R_SUCCESS) + { + strlcpy(lookup->textname, store, sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ptr; + lookup->rdtypeset = true; + default_lookups = false; + } else { + strlcpy(lookup->textname, hostname, sizeof(lookup->textname)); + usesearch = true; + } + lookup->new_search = true; + ISC_LIST_APPEND(lookup_list, lookup, link); +} + +int +main(int argc, char **argv) { + isc_result_t result; + + tries = 2; + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + fatalexit = 1; + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = host_shutdown; + + debug("main()"); + progname = argv[0]; + pre_parse_args(argc, argv); + result = isc_app_start(); + check_result(result, "isc_app_start"); + setup_libs(); + setup_system(ipv4only, ipv6only); + parse_args(false, argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + check_result(result, "isc_app_onrun"); + isc_app_run(); + cancel_all(); + destroy_libs(); + isc_app_finish(); + return ((seen_error == 0) ? 0 : 1); +} diff --git a/bin/dig/host.rst b/bin/dig/host.rst new file mode 100644 index 0000000..2647d36 --- /dev/null +++ b/bin/dig/host.rst @@ -0,0 +1,193 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: host +.. program:: host +.. _man_host: + +host - DNS lookup utility +------------------------- + +Synopsis +~~~~~~~~ + +:program:`host` [**-aACdlnrsTUwv**] [**-c** class] [**-N** ndots] [**-p** port] [**-R** number] [**-t** type] [**-W** wait] [**-m** flag] [ [**-4**] | [**-6**] ] [**-v**] [**-V**] {name} [server] + +Description +~~~~~~~~~~~ + +:program:`host` is a simple utility for performing DNS lookups. It is normally +used to convert names to IP addresses and vice versa. When no arguments +or options are given, :program:`host` prints a short summary of its +command-line arguments and options. + +``name`` is the domain name that is to be looked up. It can also be a +dotted-decimal IPv4 address or a colon-delimited IPv6 address, in which +case :program:`host` by default performs a reverse lookup for that address. +``server`` is an optional argument which is either the name or IP +address of the name server that :program:`host` should query instead of the +server or servers listed in ``/etc/resolv.conf``. + +Options +~~~~~~~ + +.. option:: -4 + + This option specifies that only IPv4 should be used for query transport. See also the :option:`-6` option. + +.. option:: -6 + + This option specifies that only IPv6 should be used for query transport. See also the :option:`-4` option. + +.. option:: -a + + The :option:`-a` ("all") option is normally equivalent to :option:`-v` :option:`-t ANY <-t>`. It + also affects the behavior of the :option:`-l` list zone option. + +.. option:: -A + + The :option:`-A` ("almost all") option is equivalent to :option:`-a`, except that RRSIG, + NSEC, and NSEC3 records are omitted from the output. + +.. option:: -c class + + This option specifies the query class, which can be used to lookup HS (Hesiod) or CH (Chaosnet) + class resource records. The default class is IN (Internet). + +.. option:: -C + + This option indicates that :iscman:`named` should check consistency, meaning that :program:`host` queries the SOA records for zone + ``name`` from all the listed authoritative name servers for that + zone. The list of name servers is defined by the NS records that are + found for the zone. + +.. option:: -d + + This option prints debugging traces, and is equivalent to the :option:`-v` verbose option. + +.. option:: -l + + This option tells :iscman:`named` to list the zone, meaning the :program:`host` command performs a zone transfer of zone + ``name`` and prints out the NS, PTR, and address records (A/AAAA). + + Together, the :option:`-l` :option:`-a` options print all records in the zone. + +.. option:: -N ndots + + This option specifies the number of dots (``ndots``) that have to be in ``name`` for it to be + considered absolute. The default value is that defined using the + ``ndots`` statement in ``/etc/resolv.conf``, or 1 if no ``ndots`` statement + is present. Names with fewer dots are interpreted as relative names, + and are searched for in the domains listed in the ``search`` or + ``domain`` directive in ``/etc/resolv.conf``. + +.. option:: -p port + + This option specifies the port to query on the server. The default is 53. + +.. option:: -r + + This option specifies a non-recursive query; setting this option clears the RD (recursion + desired) bit in the query. This means that the name server + receiving the query does not attempt to resolve ``name``. The :option:`-r` + option enables :program:`host` to mimic the behavior of a name server by + making non-recursive queries, and expecting to receive answers to + those queries that can be referrals to other name servers. + +.. option:: -R number + + This option specifies the number of retries for UDP queries. If ``number`` is negative or zero, + the number of retries is silently set to 1. The default value is 1, or + the value of the ``attempts`` option in ``/etc/resolv.conf``, if set. + +.. option:: -s + + This option tells :iscman:`named` *not* to send the query to the next nameserver if any server responds + with a SERVFAIL response, which is the reverse of normal stub + resolver behavior. + +.. option:: -t type + + This option specifies the query type. The ``type`` argument can be any recognized query type: + CNAME, NS, SOA, TXT, DNSKEY, AXFR, etc. + + When no query type is specified, :program:`host` automatically selects an + appropriate query type. By default, it looks for A, AAAA, and MX + records. If the :option:`-C` option is given, queries are made for SOA + records. If ``name`` is a dotted-decimal IPv4 address or + colon-delimited IPv6 address, :program:`host` queries for PTR records. + + If a query type of IXFR is chosen, the starting serial number can be + specified by appending an equals sign (=), followed by the starting serial + number, e.g., :option:`-t IXFR=12345678 <-t>`. + +.. option:: -T, -U + + This option specifies TCP or UDP. By default, :program:`host` uses UDP when making queries; the + :option:`-T` option makes it use a TCP connection when querying the name + server. TCP is automatically selected for queries that require + it, such as zone transfer (AXFR) requests. Type ``ANY`` queries default + to TCP, but can be forced to use UDP initially via :option:`-U`. + +.. option:: -m flag + + This option sets memory usage debugging: the flag can be ``record``, ``usage``, or + ``trace``. The :option:`-m` option can be specified more than once to set + multiple flags. + +.. option:: -v + + This option sets verbose output, and is equivalent to the :option:`-d` debug option. Verbose output + can also be enabled by setting the ``debug`` option in + ``/etc/resolv.conf``. + +.. option:: -V + + This option prints the version number and exits. + +.. option:: -w + + This option sets "wait forever": the query timeout is set to the maximum possible. See + also the :option:`-W` option. + +.. option:: -W wait + + This options sets the length of the wait timeout, indicating that :iscman:`named` should wait for up to ``wait`` seconds for a reply. If ``wait`` is + less than 1, the wait interval is set to 1 second. + + By default, :program:`host` waits for 5 seconds for UDP responses and 10 + seconds for TCP connections. These defaults can be overridden by the + ``timeout`` option in ``/etc/resolv.conf``. + + See also the :option:`-w` option. + +IDN Support +~~~~~~~~~~~ + +If :program:`host` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. :program:`host` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, define the ``IDN_DISABLE`` +environment variable. IDN support is disabled if the variable is set +when :program:`host` runs. + +Files +~~~~~ + +``/etc/resolv.conf`` + +See Also +~~~~~~~~ + +:iscman:`dig(1) `, :iscman:`named(8) `. diff --git a/bin/dig/nslookup.c b/bin/dig/nslookup.c new file mode 100644 index 0000000..a0f508d --- /dev/null +++ b/bin/dig/nslookup.c @@ -0,0 +1,977 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dighost.h" +#include "readline.h" + +static bool short_form = true, tcpmode = false, tcpmode_set = false, + identify = false, stats = true, comments = true, + section_question = true, section_answer = true, + section_authority = true, section_additional = true, recurse = true, + aaonly = false, nofail = true, default_lookups = true, + a_noanswer = false; + +static bool interactive; + +static bool in_use = false; +static char defclass[MXRD] = "IN"; +static char deftype[MXRD] = "A"; +static isc_event_t *global_event = NULL; +static int query_error = 1, print_error = 0; + +static char domainopt[DNS_NAME_MAXTEXT]; + +static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL", + "NXDOMAIN", "NOTIMP", "REFUSED", + "YXDOMAIN", "YXRRSET", "NXRRSET", + "NOTAUTH", "NOTZONE", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15", "BADVERS" }; + +static const char *rtypetext[] = { + "rtype_0 = ", /* 0 */ + "internet address = ", /* 1 */ + "nameserver = ", /* 2 */ + "md = ", /* 3 */ + "mf = ", /* 4 */ + "canonical name = ", /* 5 */ + "soa = ", /* 6 */ + "mb = ", /* 7 */ + "mg = ", /* 8 */ + "mr = ", /* 9 */ + "rtype_10 = ", /* 10 */ + "protocol = ", /* 11 */ + "name = ", /* 12 */ + "hinfo = ", /* 13 */ + "minfo = ", /* 14 */ + "mail exchanger = ", /* 15 */ + "text = ", /* 16 */ + "rp = ", /* 17 */ + "afsdb = ", /* 18 */ + "x25 address = ", /* 19 */ + "isdn address = ", /* 20 */ + "rt = ", /* 21 */ + "nsap = ", /* 22 */ + "nsap_ptr = ", /* 23 */ + "signature = ", /* 24 */ + "key = ", /* 25 */ + "px = ", /* 26 */ + "gpos = ", /* 27 */ + "has AAAA address ", /* 28 */ + "loc = ", /* 29 */ + "next = ", /* 30 */ + "rtype_31 = ", /* 31 */ + "rtype_32 = ", /* 32 */ + "service = ", /* 33 */ + "rtype_34 = ", /* 34 */ + "naptr = ", /* 35 */ + "kx = ", /* 36 */ + "cert = ", /* 37 */ + "v6 address = ", /* 38 */ + "dname = ", /* 39 */ + "rtype_40 = ", /* 40 */ + "optional = " /* 41 */ +}; + +#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0])) + +static void +getinput(isc_task_t *task, isc_event_t *event); + +static char * +rcode_totext(dns_rcode_t rcode) { + static char buf[sizeof("?65535")]; + union { + const char *consttext; + char *deconsttext; + } totext; + + if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { + snprintf(buf, sizeof(buf), "?%u", rcode); + totext.deconsttext = buf; + } else { + totext.consttext = rcodetext[rcode]; + } + return (totext.deconsttext); +} + +static void +query_finished(void) { + isc_event_t *event = global_event; + + debug("dighost_shutdown()"); + + if (!in_use) { + isc_app_shutdown(); + return; + } + + isc_task_send(global_task, &event); +} + +static void +printsoa(dns_rdata_t *rdata) { + dns_rdata_soa_t soa; + isc_result_t result; + char namebuf[DNS_NAME_FORMATSIZE]; + + result = dns_rdata_tostruct(rdata, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + + dns_name_format(&soa.origin, namebuf, sizeof(namebuf)); + printf("\torigin = %s\n", namebuf); + dns_name_format(&soa.contact, namebuf, sizeof(namebuf)); + printf("\tmail addr = %s\n", namebuf); + printf("\tserial = %u\n", soa.serial); + printf("\trefresh = %u\n", soa.refresh); + printf("\tretry = %u\n", soa.retry); + printf("\texpire = %u\n", soa.expire); + printf("\tminimum = %u\n", soa.minimum); + dns_rdata_freestruct(&soa); +} + +static void +printaddr(dns_rdata_t *rdata) { + isc_result_t result; + char text[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + isc_buffer_t b; + + isc_buffer_init(&b, text, sizeof(text)); + result = dns_rdata_totext(rdata, NULL, &b); + check_result(result, "dns_rdata_totext"); + printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b), + (char *)isc_buffer_base(&b)); +} + +static void +printrdata(dns_rdata_t *rdata) { + isc_result_t result; + isc_buffer_t *b = NULL; + unsigned int size = 1024; + bool done = false; + + if (rdata->type < N_KNOWN_RRTYPES) { + printf("%s", rtypetext[rdata->type]); + } else { + printf("rdata_%d = ", rdata->type); + } + + while (!done) { + isc_buffer_allocate(mctx, &b, size); + result = dns_rdata_totext(rdata, NULL, b); + if (result == ISC_R_SUCCESS) { + printf("%.*s\n", (int)isc_buffer_usedlength(b), + (char *)isc_buffer_base(b)); + done = true; + } else if (result != ISC_R_NOSPACE) { + check_result(result, "dns_rdata_totext"); + } + isc_buffer_free(&b); + size *= 2; + } +} + +static isc_result_t +printsection(dig_query_t *query, dns_message_t *msg, bool headers, + dns_section_t section) { + isc_result_t result, loopresult; + dns_name_t *name; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + char namebuf[DNS_NAME_FORMATSIZE]; + + UNUSED(query); + UNUSED(headers); + + debug("printsection()"); + + result = dns_message_firstname(msg, section); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + for (;;) { + name = NULL; + dns_message_currentname(msg, section, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + switch (rdata.type) { + case dns_rdatatype_a: + case dns_rdatatype_aaaa: + if (section != DNS_SECTION_ANSWER) { + goto def_short_section; + } + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("Name:\t%s\n", namebuf); + printaddr(&rdata); + break; + case dns_rdatatype_soa: + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("%s\n", namebuf); + printsoa(&rdata); + break; + default: + def_short_section: + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("%s\t", namebuf); + printrdata(&rdata); + break; + } + dns_rdata_reset(&rdata); + loopresult = dns_rdataset_next(rdataset); + } + } + result = dns_message_nextname(msg, section); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +detailsection(dig_query_t *query, dns_message_t *msg, bool headers, + dns_section_t section) { + isc_result_t result, loopresult; + dns_name_t *name; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + char namebuf[DNS_NAME_FORMATSIZE]; + + UNUSED(query); + + debug("detailsection()"); + + if (headers) { + switch (section) { + case DNS_SECTION_QUESTION: + puts(" QUESTIONS:"); + break; + case DNS_SECTION_ANSWER: + puts(" ANSWERS:"); + break; + case DNS_SECTION_AUTHORITY: + puts(" AUTHORITY RECORDS:"); + break; + case DNS_SECTION_ADDITIONAL: + puts(" ADDITIONAL RECORDS:"); + break; + } + } + + result = dns_message_firstname(msg, section); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + for (;;) { + name = NULL; + dns_message_currentname(msg, section, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (section == DNS_SECTION_QUESTION) { + dns_name_format(name, namebuf, sizeof(namebuf)); + printf("\t%s, ", namebuf); + dns_rdatatype_format(rdataset->type, namebuf, + sizeof(namebuf)); + printf("type = %s, ", namebuf); + dns_rdataclass_format(rdataset->rdclass, + namebuf, sizeof(namebuf)); + printf("class = %s\n", namebuf); + } + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + + dns_name_format(name, namebuf, sizeof(namebuf)); + printf(" -> %s\n", namebuf); + + switch (rdata.type) { + case dns_rdatatype_soa: + printsoa(&rdata); + break; + default: + printf("\t"); + printrdata(&rdata); + } + dns_rdata_reset(&rdata); + printf("\tttl = %u\n", rdataset->ttl); + loopresult = dns_rdataset_next(rdataset); + } + } + result = dns_message_nextname(msg, section); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + UNUSED(bytes); + UNUSED(from); + UNUSED(query); +} + +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(frm); + UNUSED(lookup); +} + +static void +chase_cnamechain(dns_message_t *msg, dns_name_t *qname) { + isc_result_t result; + dns_rdataset_t *rdataset; + dns_rdata_cname_t cname; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i = msg->counts[DNS_SECTION_ANSWER]; + + while (i-- > 0) { + rdataset = NULL; + result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname, + dns_rdatatype_cname, 0, NULL, + &rdataset); + if (result != ISC_R_SUCCESS) { + return; + } + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_copy(&cname.cname, qname); + dns_rdata_freestruct(&cname); + } +} + +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + char servtext[ISC_SOCKADDR_FORMATSIZE]; + + UNUSED(msgbuf); + + /* I've we've gotten this far, we've reached a server. */ + query_error = 0; + + debug("printmessage()"); + + if (!default_lookups || query->lookup->rdtype == dns_rdatatype_a) { + isc_sockaddr_format(&query->sockaddr, servtext, + sizeof(servtext)); + printf("Server:\t\t%s\n", query->userarg); + printf("Address:\t%s\n", servtext); + + puts(""); + } + + if (!short_form) { + puts("------------"); + /* detailheader(query, msg);*/ + detailsection(query, msg, true, DNS_SECTION_QUESTION); + detailsection(query, msg, true, DNS_SECTION_ANSWER); + detailsection(query, msg, true, DNS_SECTION_AUTHORITY); + detailsection(query, msg, true, DNS_SECTION_ADDITIONAL); + puts("------------"); + } + + if (msg->rcode != 0) { + char nametext[DNS_NAME_FORMATSIZE]; + dns_name_format(query->lookup->name, nametext, + sizeof(nametext)); + printf("** server can't find %s: %s\n", nametext, + rcode_totext(msg->rcode)); + debug("returning with rcode == 0"); + + /* the lookup failed */ + print_error |= 1; + return (ISC_R_SUCCESS); + } + + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) { + char namestr[DNS_NAME_FORMATSIZE]; + dig_lookup_t *lookup; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Add AAAA lookup. */ + name = dns_fixedname_initname(&fixed); + dns_name_copy(query->lookup->name, name); + chase_cnamechain(msg, name); + dns_name_format(name, namestr, sizeof(namestr)); + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_aaaa; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + } + + if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0 && + (!default_lookups || query->lookup->rdtype == dns_rdatatype_a)) + { + puts("Non-authoritative answer:"); + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) { + printsection(query, msg, headers, DNS_SECTION_ANSWER); + } else { + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) + { + a_noanswer = true; + } else if (!default_lookups || + (query->lookup->rdtype == dns_rdatatype_aaaa && + a_noanswer)) + { + printf("*** Can't find %s: No answer\n", + query->lookup->textname); + } + } + + if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) && + (query->lookup->rdtype != dns_rdatatype_a) && + (query->lookup->rdtype != dns_rdatatype_aaaa)) + { + puts("\nAuthoritative answers can be found from:"); + printsection(query, msg, headers, DNS_SECTION_AUTHORITY); + printsection(query, msg, headers, DNS_SECTION_ADDITIONAL); + } + return (ISC_R_SUCCESS); +} + +static void +show_settings(bool full, bool serv_only) { + dig_server_t *srv; + isc_sockaddr_t sockaddr; + dig_searchlist_t *listent; + isc_result_t result; + + srv = ISC_LIST_HEAD(server_list); + + while (srv != NULL) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + + result = get_address(srv->servername, port, &sockaddr); + check_result(result, "get_address"); + + isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr)); + printf("Default server: %s\nAddress: %s\n", srv->userarg, + sockstr); + if (!full) { + return; + } + srv = ISC_LIST_NEXT(srv, link); + } + if (serv_only) { + return; + } + printf("\nSet options:\n"); + printf(" %s\t\t\t%s\t\t%s\n", tcpmode ? "vc" : "novc", + short_form ? "nodebug" : "debug", debugging ? "d2" : "nod2"); + printf(" %s\t\t%s\n", usesearch ? "search" : "nosearch", + recurse ? "recurse" : "norecurse"); + printf(" timeout = %u\t\tretry = %d\tport = %u\tndots = %d\n", timeout, + tries, port, ndots); + printf(" querytype = %-8s\tclass = %s\n", deftype, defclass); + printf(" srchlist = "); + for (listent = ISC_LIST_HEAD(search_list); listent != NULL; + listent = ISC_LIST_NEXT(listent, link)) + { + printf("%s", listent->origin); + if (ISC_LIST_NEXT(listent, link) != NULL) { + printf("/"); + } + } + printf("\n"); +} + +static bool +testtype(char *typetext) { + isc_result_t result; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + + tr.base = typetext; + tr.length = strlen(typetext); + result = dns_rdatatype_fromtext(&rdtype, &tr); + if (result == ISC_R_SUCCESS) { + return (true); + } else { + printf("unknown query type: %s\n", typetext); + return (false); + } +} + +static bool +testclass(char *typetext) { + isc_result_t result; + isc_textregion_t tr; + dns_rdataclass_t rdclass; + + tr.base = typetext; + tr.length = strlen(typetext); + result = dns_rdataclass_fromtext(&rdclass, &tr); + if (result == ISC_R_SUCCESS) { + return (true); + } else { + printf("unknown query class: %s\n", typetext); + return (false); + } +} + +static void +set_port(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, 65535, "port"); + if (result == ISC_R_SUCCESS) { + port = (uint16_t)n; + port_set = true; + } +} + +static void +set_timeout(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout"); + if (result == ISC_R_SUCCESS) { + timeout = n; + } +} + +static void +set_tries(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, INT_MAX, "tries"); + if (result == ISC_R_SUCCESS) { + tries = n; + } +} + +static void +set_ndots(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, 128, "ndots"); + if (result == ISC_R_SUCCESS) { + ndots = n; + } +} + +static void +version(void) { + fprintf(stderr, "nslookup %s\n", PACKAGE_VERSION); +} + +static void +setoption(char *opt) { + size_t l = strlen(opt); + +#define CHECKOPT(A, N) \ + ((l >= N) && (l < sizeof(A)) && (strncasecmp(opt, A, l) == 0)) + + if (CHECKOPT("all", 3)) { + show_settings(true, false); + } else if (strncasecmp(opt, "class=", 6) == 0) { + if (testclass(&opt[6])) { + strlcpy(defclass, &opt[6], sizeof(defclass)); + } + } else if (strncasecmp(opt, "cl=", 3) == 0) { + if (testclass(&opt[3])) { + strlcpy(defclass, &opt[3], sizeof(defclass)); + } + } else if (strncasecmp(opt, "type=", 5) == 0) { + if (testtype(&opt[5])) { + strlcpy(deftype, &opt[5], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "ty=", 3) == 0) { + if (testtype(&opt[3])) { + strlcpy(deftype, &opt[3], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "querytype=", 10) == 0) { + if (testtype(&opt[10])) { + strlcpy(deftype, &opt[10], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "query=", 6) == 0) { + if (testtype(&opt[6])) { + strlcpy(deftype, &opt[6], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "qu=", 3) == 0) { + if (testtype(&opt[3])) { + strlcpy(deftype, &opt[3], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "q=", 2) == 0) { + if (testtype(&opt[2])) { + strlcpy(deftype, &opt[2], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "domain=", 7) == 0) { + strlcpy(domainopt, &opt[7], sizeof(domainopt)); + set_search_domain(domainopt); + usesearch = true; + } else if (strncasecmp(opt, "do=", 3) == 0) { + strlcpy(domainopt, &opt[3], sizeof(domainopt)); + set_search_domain(domainopt); + usesearch = true; + } else if (strncasecmp(opt, "port=", 5) == 0) { + set_port(&opt[5]); + } else if (strncasecmp(opt, "po=", 3) == 0) { + set_port(&opt[3]); + } else if (strncasecmp(opt, "timeout=", 8) == 0) { + set_timeout(&opt[8]); + } else if (strncasecmp(opt, "t=", 2) == 0) { + set_timeout(&opt[2]); + } else if (CHECKOPT("recurse", 3)) { + recurse = true; + } else if (CHECKOPT("norecurse", 5)) { + recurse = false; + } else if (strncasecmp(opt, "retry=", 6) == 0) { + set_tries(&opt[6]); + } else if (strncasecmp(opt, "ret=", 4) == 0) { + set_tries(&opt[4]); + } else if (CHECKOPT("defname", 3)) { + usesearch = true; + } else if (CHECKOPT("nodefname", 5)) { + usesearch = false; + } else if (CHECKOPT("vc", 2)) { + tcpmode = true; + tcpmode_set = true; + } else if (CHECKOPT("novc", 4)) { + tcpmode = false; + tcpmode_set = true; + } else if (CHECKOPT("debug", 3)) { + short_form = false; + showsearch = true; + } else if (CHECKOPT("nodebug", 5)) { + short_form = true; + showsearch = false; + } else if (CHECKOPT("d2", 2)) { + debugging = true; + } else if (CHECKOPT("nod2", 4)) { + debugging = false; + } else if (CHECKOPT("search", 3)) { + usesearch = true; + } else if (CHECKOPT("nosearch", 5)) { + usesearch = false; + } else if (CHECKOPT("sil", 3)) { + /* deprecation_msg = false; */ + } else if (CHECKOPT("fail", 3)) { + nofail = false; + } else if (CHECKOPT("nofail", 5)) { + nofail = true; + } else if (strncasecmp(opt, "ndots=", 6) == 0) { + set_ndots(&opt[6]); + } else { + printf("*** Invalid option: %s\n", opt); + } +} + +static void +addlookup(char *opt) { + dig_lookup_t *lookup; + isc_result_t result; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + char store[MXNAME]; + + debug("addlookup()"); + + a_noanswer = false; + + tr.base = deftype; + tr.length = strlen(deftype); + result = dns_rdatatype_fromtext(&rdtype, &tr); + if (result != ISC_R_SUCCESS) { + printf("unknown query type: %s\n", deftype); + rdclass = dns_rdatatype_a; + } + tr.base = defclass; + tr.length = strlen(defclass); + result = dns_rdataclass_fromtext(&rdclass, &tr); + if (result != ISC_R_SUCCESS) { + printf("unknown query class: %s\n", defclass); + rdclass = dns_rdataclass_in; + } + lookup = make_empty_lookup(); + if (get_reverse(store, sizeof(store), opt, true) == ISC_R_SUCCESS) { + strlcpy(lookup->textname, store, sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ptr; + lookup->rdtypeset = true; + } else { + strlcpy(lookup->textname, opt, sizeof(lookup->textname)); + lookup->rdtype = rdtype; + lookup->rdtypeset = true; + } + lookup->rdclass = rdclass; + lookup->rdclassset = true; + lookup->trace = false; + lookup->trace_root = lookup->trace; + lookup->ns_search_only = false; + lookup->identify = identify; + lookup->recurse = recurse; + lookup->aaonly = aaonly; + lookup->retries = tries; + lookup->setqid = false; + lookup->qid = 0; + lookup->comments = comments; + if (lookup->rdtype == dns_rdatatype_any && !tcpmode_set) { + lookup->tcp_mode = true; + } else { + lookup->tcp_mode = tcpmode; + } + lookup->stats = stats; + lookup->section_question = section_question; + lookup->section_answer = section_answer; + lookup->section_authority = section_authority; + lookup->section_additional = section_additional; + lookup->new_search = true; + lookup->besteffort = false; + if (nofail) { + lookup->servfail_stops = false; + } + ISC_LIST_INIT(lookup->q); + ISC_LINK_INIT(lookup, link); + ISC_LIST_APPEND(lookup_list, lookup, link); + lookup->origin = NULL; + ISC_LIST_INIT(lookup->my_server_list); + debug("looking up %s", lookup->textname); +} + +static void +do_next_command(char *input) { + char *ptr, *arg, *last; + + if ((ptr = strtok_r(input, " \t\r\n", &last)) == NULL) { + return; + } + arg = strtok_r(NULL, " \t\r\n", &last); + if ((strcasecmp(ptr, "set") == 0) && (arg != NULL)) { + setoption(arg); + } else if ((strcasecmp(ptr, "server") == 0) || + (strcasecmp(ptr, "lserver") == 0)) + { + isc_app_block(); + set_nameserver(arg); + check_ra = false; + isc_app_unblock(); + show_settings(true, true); + } else if (strcasecmp(ptr, "exit") == 0) { + in_use = false; + } else if (strcasecmp(ptr, "help") == 0 || strcasecmp(ptr, "?") == 0) { + printf("The '%s' command is not yet implemented.\n", ptr); + } else if (strcasecmp(ptr, "finger") == 0 || + strcasecmp(ptr, "root") == 0 || strcasecmp(ptr, "ls") == 0 || + strcasecmp(ptr, "view") == 0) + { + printf("The '%s' command is not implemented.\n", ptr); + } else { + addlookup(ptr); + } +} + +static void +get_next_command(void) { + char cmdlinebuf[COMMSIZE]; + char *cmdline, *ptr = NULL; + + isc_app_block(); + if (interactive) { + cmdline = ptr = readline("> "); + if (ptr != NULL && *ptr != 0) { + add_history(ptr); + } + } else { + cmdline = fgets(cmdlinebuf, COMMSIZE, stdin); + } + isc_app_unblock(); + if (cmdline == NULL) { + in_use = false; + } else { + do_next_command(cmdline); + } + if (ptr != NULL) { + free(ptr); + } +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " nslookup [-opt ...] # interactive mode " + "using default server\n"); + fprintf(stderr, " nslookup [-opt ...] - server # interactive mode " + "using 'server'\n"); + fprintf(stderr, " nslookup [-opt ...] host # just look up " + "'host' using default server\n"); + fprintf(stderr, " nslookup [-opt ...] host server # just look up " + "'host' using 'server'\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) { + bool have_lookup = false; + + usesearch = true; + for (argc--, argv++; argc > 0 && argv[0] != NULL; argc--, argv++) { + debug("main parsing %s", argv[0]); + if (argv[0][0] == '-') { + if (strncasecmp(argv[0], "-ver", 4) == 0) { + version(); + exit(0); + } else if (argv[0][1] != 0) { + setoption(&argv[0][1]); + } else { + have_lookup = true; + } + } else { + if (!have_lookup) { + have_lookup = true; + in_use = true; + addlookup(argv[0]); + } else { + if (argv[1] != NULL) { + usage(); + } + set_nameserver(argv[0]); + check_ra = false; + } + } + } +} + +static void +getinput(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + if (global_event == NULL) { + global_event = event; + } + while (in_use) { + get_next_command(); + if (ISC_LIST_HEAD(lookup_list) != NULL) { + start_lookup(); + return; + } + } + isc_app_shutdown(); +} + +int +main(int argc, char **argv) { + isc_result_t result; + + interactive = isatty(0); + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + check_ra = true; + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = query_finished; + + result = isc_app_start(); + check_result(result, "isc_app_start"); + + setup_libs(); + progname = argv[0]; + + setup_system(false, false); + parse_args(argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + if (domainopt[0] != '\0') { + set_search_domain(domainopt); + } + if (in_use) { + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + } else { + result = isc_app_onrun(mctx, global_task, getinput, NULL); + } + check_result(result, "isc_app_onrun"); + in_use = !in_use; + + (void)isc_app_run(); + + puts(""); + debug("done, and starting to shut down"); + if (global_event != NULL) { + isc_event_free(&global_event); + } + cancel_all(); + destroy_libs(); + isc_app_finish(); + + return (query_error | print_error); +} diff --git a/bin/dig/nslookup.rst b/bin/dig/nslookup.rst new file mode 100644 index 0000000..2b92aa7 --- /dev/null +++ b/bin/dig/nslookup.rst @@ -0,0 +1,208 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: nslookup +.. program:: nslookup +.. _man_nslookup: + +nslookup - query Internet name servers interactively +---------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`nslookup` [-option] [name | -] [server] + +Description +~~~~~~~~~~~ + +:program:`nslookup` is a program to query Internet domain name servers. +:program:`nslookup` has two modes: interactive and non-interactive. Interactive +mode allows the user to query name servers for information about various +hosts and domains or to print a list of hosts in a domain. +Non-interactive mode prints just the name and requested +information for a host or domain. + +Arguments +~~~~~~~~~ + +Interactive mode is entered in the following cases: + +a. when no arguments are given (the default name server is used); + +b. when the first argument is a hyphen (-) and the second argument is + the host name or Internet address of a name server. + +Non-interactive mode is used when the name or Internet address of the +host to be looked up is given as the first argument. The optional second +argument specifies the host name or address of a name server. + +Options can also be specified on the command line if they precede the +arguments and are prefixed with a hyphen. For example, to change the +default query type to host information, with an initial timeout of 10 +seconds, type: + +:: + + nslookup -query=hinfo -timeout=10 + +The ``-version`` option causes :program:`nslookup` to print the version number +and immediately exit. + +Interactive Commands +~~~~~~~~~~~~~~~~~~~~ + +``host [server]`` + This command looks up information for :iscman:`host` using the current default server or + using ``server``, if specified. If :iscman:`host` is an Internet address and the + query type is A or PTR, the name of the host is returned. If :iscman:`host` is + a name and does not have a trailing period (``.``), the search list is used + to qualify the name. + + To look up a host not in the current domain, append a period to the + name. + +``server domain`` | ``lserver domain`` + These commands change the default server to ``domain``; ``lserver`` uses the initial + server to look up information about ``domain``, while ``server`` uses the + current default server. If an authoritative answer cannot be found, + the names of servers that might have the answer are returned. + +``root`` + This command is not implemented. + +``finger`` + This command is not implemented. + +``ls`` + This command is not implemented. + +``view`` + This command is not implemented. + +``help`` + This command is not implemented. + +``?`` + This command is not implemented. + +``exit`` + This command exits the program. + +``set keyword[=value]`` + This command is used to change state information that affects the + lookups. Valid keywords are: + + ``all`` + This keyword prints the current values of the frequently used options to + ``set``. Information about the current default server and host is + also printed. + + ``class=value`` + This keyword changes the query class to one of: + + ``IN`` + the Internet class + + ``CH`` + the Chaos class + + ``HS`` + the Hesiod class + + ``ANY`` + wildcard + + The class specifies the protocol group of the information. The default + is ``IN``; the abbreviation for this keyword is ``cl``. + + ``nodebug`` + This keyword turns on or off the display of the full response packet, and any + intermediate response packets, when searching. The default for this keyword is + ``nodebug``; the abbreviation for this keyword is ``[no]deb``. + + ``nod2`` + This keyword turns debugging mode on or off. This displays more about what + nslookup is doing. The default is ``nod2``. + + ``domain=name`` + This keyword sets the search list to ``name``. + + ``nosearch`` + If the lookup request contains at least one period, but does not end + with a trailing period, this keyword appends the domain names in the domain + search list to the request until an answer is received. The default is ``search``. + + ``port=value`` + This keyword changes the default TCP/UDP name server port to ``value`` from + its default, port 53. The abbreviation for this keyword is ``po``. + + ``querytype=value`` | ``type=value`` + This keyword changes the type of the information query to ``value``. The + defaults are A and then AAAA; the abbreviations for these keywords are + ``q`` and ``ty``. + + Please note that it is only possible to specify one query type. Only the default + behavior looks up both when an alternative is not specified. + + ``norecurse`` + This keyword tells the name server to query other servers if it does not have + the information. The default is ``recurse``; the abbreviation for this + keyword is ``[no]rec``. + + ``ndots=number`` + This keyword sets the number of dots (label separators) in a domain that + disables searching. Absolute names always stop searching. + + ``retry=number`` + This keyword sets the number of retries to ``number``. + + ``timeout=number`` + This keyword changes the initial timeout interval to wait for a reply to + ``number``, in seconds. + + ``novc`` + This keyword indicates that a virtual circuit should always be used when sending requests to the server. + ``novc`` is the default. + + ``nofail`` + This keyword tries the next nameserver if a nameserver responds with SERVFAIL or + a referral (nofail), or terminates the query (fail) on such a response. The + default is ``nofail``. + +Return Values +~~~~~~~~~~~~~ + +:program:`nslookup` returns with an exit status of 1 if any query failed, and 0 +otherwise. + +IDN Support +~~~~~~~~~~~ + +If :program:`nslookup` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. :program:`nslookup` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, define the ``IDN_DISABLE`` +environment variable. IDN support is disabled if the variable is set +when :program:`nslookup` runs, or when the standard output is not a tty. + +Files +~~~~~ + +``/etc/resolv.conf`` + +See Also +~~~~~~~~ + +:iscman:`dig(1) `, :iscman:`host(1) `, :iscman:`named(8) `. diff --git a/bin/dig/readline.h b/bin/dig/readline.h new file mode 100644 index 0000000..68a38c3 --- /dev/null +++ b/bin/dig/readline.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/* + * A little wrapper around readline(), add_history() and free() to make using + * the readline code simpler. + */ + +#if defined(HAVE_READLINE_LIBEDIT) +#include +#elif defined(HAVE_READLINE_EDITLINE) +#include +#elif defined(HAVE_READLINE_READLINE) +/* Prevent deprecated functions being declared. */ +#define _FUNCTION_DEF 1 +/* Ensure rl_message() gets prototype. */ +#define USE_VARARGS 1 +#define PREFER_STDARG 1 +#include +#include +#endif + +#if !defined(HAVE_READLINE_LIBEDIT) && !defined(HAVE_READLINE_EDITLINE) && \ + !defined(HAVE_READLINE_READLINE) + +#include +#include + +#define RL_MAXCMD (128 * 1024) + +static inline char * +readline(const char *prompt) { + char *line, *buf = malloc(RL_MAXCMD); + fprintf(stdout, "%s", prompt); + fflush(stdout); + line = fgets(buf, RL_MAXCMD, stdin); + if (line == NULL) { + free(buf); + return (NULL); + } + return (buf); +}; + +#define add_history(line) + +#endif diff --git a/bin/dnssec/Makefile.am b/bin/dnssec/Makefile.am new file mode 100644 index 0000000..32c4626 --- /dev/null +++ b/bin/dnssec/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) + +AM_CPPFLAGS += \ + -DNAMED_CONFFILE=\"${sysconfdir}/named.conf\" + +noinst_LTLIBRARIES = libdnssectool.la + +LDADD += \ + libdnssectool.la \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +bin_PROGRAMS = \ + dnssec-cds \ + dnssec-dsfromkey \ + dnssec-importkey \ + dnssec-keyfromlabel \ + dnssec-keygen \ + dnssec-revoke \ + dnssec-settime \ + dnssec-signzone \ + dnssec-verify + +libdnssectool_la_SOURCES = \ + dnssectool.h \ + dnssectool.c + +dnssec_keygen_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISCCFG_CFLAGS) + +dnssec_keygen_LDADD = \ + $(LDADD) \ + $(LIBISCCFG_LIBS) diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in new file mode 100644 index 0000000..47c7352 --- /dev/null +++ b/bin/dnssec/Makefile.in @@ -0,0 +1,976 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +bin_PROGRAMS = dnssec-cds$(EXEEXT) dnssec-dsfromkey$(EXEEXT) \ + dnssec-importkey$(EXEEXT) dnssec-keyfromlabel$(EXEEXT) \ + dnssec-keygen$(EXEEXT) dnssec-revoke$(EXEEXT) \ + dnssec-settime$(EXEEXT) dnssec-signzone$(EXEEXT) \ + dnssec-verify$(EXEEXT) +subdir = bin/dnssec +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +libdnssectool_la_LIBADD = +am_libdnssectool_la_OBJECTS = dnssectool.lo +libdnssectool_la_OBJECTS = $(am_libdnssectool_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +dnssec_cds_SOURCES = dnssec-cds.c +dnssec_cds_OBJECTS = dnssec-cds.$(OBJEXT) +dnssec_cds_LDADD = $(LDADD) +dnssec_cds_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_dsfromkey_SOURCES = dnssec-dsfromkey.c +dnssec_dsfromkey_OBJECTS = dnssec-dsfromkey.$(OBJEXT) +dnssec_dsfromkey_LDADD = $(LDADD) +dnssec_dsfromkey_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_importkey_SOURCES = dnssec-importkey.c +dnssec_importkey_OBJECTS = dnssec-importkey.$(OBJEXT) +dnssec_importkey_LDADD = $(LDADD) +dnssec_importkey_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_keyfromlabel_SOURCES = dnssec-keyfromlabel.c +dnssec_keyfromlabel_OBJECTS = dnssec-keyfromlabel.$(OBJEXT) +dnssec_keyfromlabel_LDADD = $(LDADD) +dnssec_keyfromlabel_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_keygen_SOURCES = dnssec-keygen.c +dnssec_keygen_OBJECTS = dnssec_keygen-dnssec-keygen.$(OBJEXT) +dnssec_keygen_DEPENDENCIES = $(LDADD) $(LIBISCCFG_LIBS) +dnssec_revoke_SOURCES = dnssec-revoke.c +dnssec_revoke_OBJECTS = dnssec-revoke.$(OBJEXT) +dnssec_revoke_LDADD = $(LDADD) +dnssec_revoke_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_settime_SOURCES = dnssec-settime.c +dnssec_settime_OBJECTS = dnssec-settime.$(OBJEXT) +dnssec_settime_LDADD = $(LDADD) +dnssec_settime_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_signzone_SOURCES = dnssec-signzone.c +dnssec_signzone_OBJECTS = dnssec-signzone.$(OBJEXT) +dnssec_signzone_LDADD = $(LDADD) +dnssec_signzone_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +dnssec_verify_SOURCES = dnssec-verify.c +dnssec_verify_OBJECTS = dnssec-verify.$(OBJEXT) +dnssec_verify_LDADD = $(LDADD) +dnssec_verify_DEPENDENCIES = libdnssectool.la $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/dnssec-cds.Po \ + ./$(DEPDIR)/dnssec-dsfromkey.Po \ + ./$(DEPDIR)/dnssec-importkey.Po \ + ./$(DEPDIR)/dnssec-keyfromlabel.Po \ + ./$(DEPDIR)/dnssec-revoke.Po ./$(DEPDIR)/dnssec-settime.Po \ + ./$(DEPDIR)/dnssec-signzone.Po ./$(DEPDIR)/dnssec-verify.Po \ + ./$(DEPDIR)/dnssec_keygen-dnssec-keygen.Po \ + ./$(DEPDIR)/dnssectool.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdnssectool_la_SOURCES) dnssec-cds.c dnssec-dsfromkey.c \ + dnssec-importkey.c dnssec-keyfromlabel.c dnssec-keygen.c \ + dnssec-revoke.c dnssec-settime.c dnssec-signzone.c \ + dnssec-verify.c +DIST_SOURCES = $(libdnssectool_la_SOURCES) dnssec-cds.c \ + dnssec-dsfromkey.c dnssec-importkey.c dnssec-keyfromlabel.c \ + dnssec-keygen.c dnssec-revoke.c dnssec-settime.c \ + dnssec-signzone.c dnssec-verify.c +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + -DNAMED_CONFFILE=\"${sysconfdir}/named.conf\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = libdnssectool.la $(LIBISC_LIBS) $(LIBDNS_LIBS) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +noinst_LTLIBRARIES = libdnssectool.la +libdnssectool_la_SOURCES = \ + dnssectool.h \ + dnssectool.c + +dnssec_keygen_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISCCFG_CFLAGS) + +dnssec_keygen_LDADD = \ + $(LDADD) \ + $(LIBISCCFG_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/dnssec/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/dnssec/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdnssectool.la: $(libdnssectool_la_OBJECTS) $(libdnssectool_la_DEPENDENCIES) $(EXTRA_libdnssectool_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libdnssectool_la_OBJECTS) $(libdnssectool_la_LIBADD) $(LIBS) + +dnssec-cds$(EXEEXT): $(dnssec_cds_OBJECTS) $(dnssec_cds_DEPENDENCIES) $(EXTRA_dnssec_cds_DEPENDENCIES) + @rm -f dnssec-cds$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_cds_OBJECTS) $(dnssec_cds_LDADD) $(LIBS) + +dnssec-dsfromkey$(EXEEXT): $(dnssec_dsfromkey_OBJECTS) $(dnssec_dsfromkey_DEPENDENCIES) $(EXTRA_dnssec_dsfromkey_DEPENDENCIES) + @rm -f dnssec-dsfromkey$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_dsfromkey_OBJECTS) $(dnssec_dsfromkey_LDADD) $(LIBS) + +dnssec-importkey$(EXEEXT): $(dnssec_importkey_OBJECTS) $(dnssec_importkey_DEPENDENCIES) $(EXTRA_dnssec_importkey_DEPENDENCIES) + @rm -f dnssec-importkey$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_importkey_OBJECTS) $(dnssec_importkey_LDADD) $(LIBS) + +dnssec-keyfromlabel$(EXEEXT): $(dnssec_keyfromlabel_OBJECTS) $(dnssec_keyfromlabel_DEPENDENCIES) $(EXTRA_dnssec_keyfromlabel_DEPENDENCIES) + @rm -f dnssec-keyfromlabel$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_keyfromlabel_OBJECTS) $(dnssec_keyfromlabel_LDADD) $(LIBS) + +dnssec-keygen$(EXEEXT): $(dnssec_keygen_OBJECTS) $(dnssec_keygen_DEPENDENCIES) $(EXTRA_dnssec_keygen_DEPENDENCIES) + @rm -f dnssec-keygen$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_keygen_OBJECTS) $(dnssec_keygen_LDADD) $(LIBS) + +dnssec-revoke$(EXEEXT): $(dnssec_revoke_OBJECTS) $(dnssec_revoke_DEPENDENCIES) $(EXTRA_dnssec_revoke_DEPENDENCIES) + @rm -f dnssec-revoke$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_revoke_OBJECTS) $(dnssec_revoke_LDADD) $(LIBS) + +dnssec-settime$(EXEEXT): $(dnssec_settime_OBJECTS) $(dnssec_settime_DEPENDENCIES) $(EXTRA_dnssec_settime_DEPENDENCIES) + @rm -f dnssec-settime$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_settime_OBJECTS) $(dnssec_settime_LDADD) $(LIBS) + +dnssec-signzone$(EXEEXT): $(dnssec_signzone_OBJECTS) $(dnssec_signzone_DEPENDENCIES) $(EXTRA_dnssec_signzone_DEPENDENCIES) + @rm -f dnssec-signzone$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_signzone_OBJECTS) $(dnssec_signzone_LDADD) $(LIBS) + +dnssec-verify$(EXEEXT): $(dnssec_verify_OBJECTS) $(dnssec_verify_DEPENDENCIES) $(EXTRA_dnssec_verify_DEPENDENCIES) + @rm -f dnssec-verify$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dnssec_verify_OBJECTS) $(dnssec_verify_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-cds.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-dsfromkey.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-importkey.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-keyfromlabel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-revoke.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-settime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-signzone.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec-verify.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssec_keygen-dnssec-keygen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnssectool.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +dnssec_keygen-dnssec-keygen.o: dnssec-keygen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dnssec_keygen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dnssec_keygen-dnssec-keygen.o -MD -MP -MF $(DEPDIR)/dnssec_keygen-dnssec-keygen.Tpo -c -o dnssec_keygen-dnssec-keygen.o `test -f 'dnssec-keygen.c' || echo '$(srcdir)/'`dnssec-keygen.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dnssec_keygen-dnssec-keygen.Tpo $(DEPDIR)/dnssec_keygen-dnssec-keygen.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dnssec-keygen.c' object='dnssec_keygen-dnssec-keygen.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dnssec_keygen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dnssec_keygen-dnssec-keygen.o `test -f 'dnssec-keygen.c' || echo '$(srcdir)/'`dnssec-keygen.c + +dnssec_keygen-dnssec-keygen.obj: dnssec-keygen.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dnssec_keygen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dnssec_keygen-dnssec-keygen.obj -MD -MP -MF $(DEPDIR)/dnssec_keygen-dnssec-keygen.Tpo -c -o dnssec_keygen-dnssec-keygen.obj `if test -f 'dnssec-keygen.c'; then $(CYGPATH_W) 'dnssec-keygen.c'; else $(CYGPATH_W) '$(srcdir)/dnssec-keygen.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dnssec_keygen-dnssec-keygen.Tpo $(DEPDIR)/dnssec_keygen-dnssec-keygen.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dnssec-keygen.c' object='dnssec_keygen-dnssec-keygen.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dnssec_keygen_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dnssec_keygen-dnssec-keygen.obj `if test -f 'dnssec-keygen.c'; then $(CYGPATH_W) 'dnssec-keygen.c'; else $(CYGPATH_W) '$(srcdir)/dnssec-keygen.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/dnssec-cds.Po + -rm -f ./$(DEPDIR)/dnssec-dsfromkey.Po + -rm -f ./$(DEPDIR)/dnssec-importkey.Po + -rm -f ./$(DEPDIR)/dnssec-keyfromlabel.Po + -rm -f ./$(DEPDIR)/dnssec-revoke.Po + -rm -f ./$(DEPDIR)/dnssec-settime.Po + -rm -f ./$(DEPDIR)/dnssec-signzone.Po + -rm -f ./$(DEPDIR)/dnssec-verify.Po + -rm -f ./$(DEPDIR)/dnssec_keygen-dnssec-keygen.Po + -rm -f ./$(DEPDIR)/dnssectool.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/dnssec-cds.Po + -rm -f ./$(DEPDIR)/dnssec-dsfromkey.Po + -rm -f ./$(DEPDIR)/dnssec-importkey.Po + -rm -f ./$(DEPDIR)/dnssec-keyfromlabel.Po + -rm -f ./$(DEPDIR)/dnssec-revoke.Po + -rm -f ./$(DEPDIR)/dnssec-settime.Po + -rm -f ./$(DEPDIR)/dnssec-signzone.Po + -rm -f ./$(DEPDIR)/dnssec-verify.Po + -rm -f ./$(DEPDIR)/dnssec_keygen-dnssec-keygen.Po + -rm -f ./$(DEPDIR)/dnssectool.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-binPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am test-am test-local uninstall uninstall-am \ + uninstall-binPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/dnssec/dnssec-cds.c b/bin/dnssec/dnssec-cds.c new file mode 100644 index 0000000..06b1f8c --- /dev/null +++ b/bin/dnssec/dnssec-cds.c @@ -0,0 +1,1361 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Written by Tony Finch + * at Cambridge University Information Services + */ + +/*! \file */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-cds"; + +/* + * Infrastructure + */ +static isc_log_t *lctx = NULL; +static isc_mem_t *mctx = NULL; + +/* + * The domain we are working on + */ +static const char *namestr = NULL; +static dns_fixedname_t fixed; +static dns_name_t *name = NULL; +static dns_rdataclass_t rdclass = dns_rdataclass_in; + +static const char *startstr = NULL; /* from which we derive notbefore */ +static isc_stdtime_t notbefore = 0; /* restrict sig inception times */ +static dns_rdata_rrsig_t oldestsig; /* for recording inception time */ + +static int nkey; /* number of child zone DNSKEY records */ + +/* + * The validation strategy of this program is top-down. + * + * We start with an implicitly trusted authoritative dsset. + * + * The child DNSKEY RRset is scanned to find out which keys are + * authenticated by DS records, and the result is recorded in a key + * table as described later in this comment. + * + * The key table is used up to three times to verify the signatures on + * the child DNSKEY, CDNSKEY, and CDS RRsets. In this program, only keys + * that have matching DS records are used for validating signatures. + * + * For replay attack protection, signatures are ignored if their inception + * time is before the previously recorded inception time. We use the earliest + * signature so that another run of dnssec-cds with the same records will + * still accept all the signatures. + * + * A key table is an array of nkey keyinfo structures, like + * + * keyinfo_t key_tbl[nkey]; + * + * Each key is decoded into more useful representations, held in + * keyinfo->rdata + * keyinfo->dst + * + * If a key has no matching DS record then keyinfo->dst is NULL. + * + * The key algorithm and ID are saved in keyinfo->algo and + * keyinfo->tag for quicky skipping DS and RRSIG records that can't + * match. + */ +typedef struct keyinfo { + dns_rdata_t rdata; + dst_key_t *dst; + dns_secalg_t algo; + dns_keytag_t tag; +} keyinfo_t; + +/* A replaceable function that can generate a DS RRset from some input */ +typedef isc_result_t +ds_maker_func_t(isc_buffer_t *buf, dns_rdata_t *ds, dns_dsdigest_t dt, + dns_rdata_t *crdata); + +static dns_rdataset_t cdnskey_set = DNS_RDATASET_INIT; +static dns_rdataset_t cdnskey_sig = DNS_RDATASET_INIT; +static dns_rdataset_t cds_set = DNS_RDATASET_INIT; +static dns_rdataset_t cds_sig = DNS_RDATASET_INIT; +static dns_rdataset_t dnskey_set = DNS_RDATASET_INIT; +static dns_rdataset_t dnskey_sig = DNS_RDATASET_INIT; +static dns_rdataset_t old_ds_set = DNS_RDATASET_INIT; +static dns_rdataset_t new_ds_set = DNS_RDATASET_INIT; + +static keyinfo_t *old_key_tbl = NULL, *new_key_tbl = NULL; + +isc_buffer_t *new_ds_buf = NULL; /* backing store for new_ds_set */ + +static dns_db_t *child_db = NULL; +static dns_dbnode_t *child_node = NULL; +static dns_db_t *parent_db = NULL; +static dns_dbnode_t *parent_node = NULL; +static dns_db_t *update_db = NULL; +static dns_dbnode_t *update_node = NULL; +static dns_dbversion_t *update_version = NULL; +static bool cleanup_dst = false; +static bool print_mem_stats = false; + +static void +verbose_time(int level, const char *msg, isc_stdtime_t time) { + isc_result_t result; + isc_buffer_t timebuf; + char timestr[32]; + + if (verbose < level) { + return; + } + + isc_buffer_init(&timebuf, timestr, sizeof(timestr)); + result = dns_time64_totext(time, &timebuf); + check_result(result, "dns_time64_totext()"); + isc_buffer_putuint8(&timebuf, 0); + if (verbose < 3) { + vbprintf(level, "%s %s\n", msg, timestr); + } else { + vbprintf(level, "%s %s (%" PRIu32 ")\n", msg, timestr, time); + } +} + +static void +initname(char *setname) { + isc_result_t result; + isc_buffer_t buf; + + name = dns_fixedname_initname(&fixed); + namestr = setname; + + isc_buffer_init(&buf, setname, strlen(setname)); + isc_buffer_add(&buf, strlen(setname)); + result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize name %s", setname); + } +} + +static void +findset(dns_db_t *db, dns_dbnode_t *node, dns_rdatatype_t type, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { + isc_result_t result; + + dns_rdataset_init(rdataset); + if (sigrdataset != NULL) { + dns_rdataset_init(sigrdataset); + } + result = dns_db_findrdataset(db, node, NULL, type, 0, 0, rdataset, + sigrdataset); + if (result != ISC_R_NOTFOUND) { + check_result(result, "dns_db_findrdataset()"); + } +} + +static void +freeset(dns_rdataset_t *rdataset) { + if (dns_rdataset_isassociated(rdataset)) { + dns_rdataset_disassociate(rdataset); + } +} + +static void +freelist(dns_rdataset_t *rdataset) { + dns_rdatalist_t *rdlist; + dns_rdata_t *rdata; + + if (!dns_rdataset_isassociated(rdataset)) { + return; + } + + dns_rdatalist_fromrdataset(rdataset, &rdlist); + + for (rdata = ISC_LIST_HEAD(rdlist->rdata); rdata != NULL; + rdata = ISC_LIST_HEAD(rdlist->rdata)) + { + ISC_LIST_UNLINK(rdlist->rdata, rdata, link); + isc_mem_put(mctx, rdata, sizeof(*rdata)); + } + isc_mem_put(mctx, rdlist, sizeof(*rdlist)); + dns_rdataset_disassociate(rdataset); +} + +static void +free_all_sets(void) { + freeset(&cdnskey_set); + freeset(&cdnskey_sig); + freeset(&cds_set); + freeset(&cds_sig); + freeset(&dnskey_set); + freeset(&dnskey_sig); + freeset(&old_ds_set); + freelist(&new_ds_set); + if (new_ds_buf != NULL) { + isc_buffer_free(&new_ds_buf); + } +} + +static void +load_db(const char *filename, dns_db_t **dbp, dns_dbnode_t **nodep) { + isc_result_t result; + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, dbp); + check_result(result, "dns_db_create()"); + + result = dns_db_load(*dbp, filename, dns_masterformat_text, + DNS_MASTER_HINT); + if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { + fatal("can't load %s: %s", filename, isc_result_totext(result)); + } + + result = dns_db_findnode(*dbp, name, false, nodep); + if (result != ISC_R_SUCCESS) { + fatal("can't find %s node in %s", namestr, filename); + } +} + +static void +free_db(dns_db_t **dbp, dns_dbnode_t **nodep, dns_dbversion_t **versionp) { + if (*dbp != NULL) { + if (*nodep != NULL) { + dns_db_detachnode(*dbp, nodep); + } + if (versionp != NULL && *versionp != NULL) { + dns_db_closeversion(*dbp, versionp, false); + } + dns_db_detach(dbp); + } +} + +static void +load_child_sets(const char *file) { + load_db(file, &child_db, &child_node); + findset(child_db, child_node, dns_rdatatype_dnskey, &dnskey_set, + &dnskey_sig); + findset(child_db, child_node, dns_rdatatype_cdnskey, &cdnskey_set, + &cdnskey_sig); + findset(child_db, child_node, dns_rdatatype_cds, &cds_set, &cds_sig); + free_db(&child_db, &child_node, NULL); +} + +static void +get_dsset_name(char *filename, size_t size, const char *path, + const char *suffix) { + isc_result_t result; + isc_buffer_t buf; + size_t len; + + isc_buffer_init(&buf, filename, size); + + len = strlen(path); + + /* allow room for a trailing slash */ + if (isc_buffer_availablelength(&buf) <= len) { + fatal("%s: pathname too long", path); + } + isc_buffer_putstr(&buf, path); + + if (isc_file_isdirectory(path) == ISC_R_SUCCESS) { + const char *prefix = "dsset-"; + + if (path[len - 1] != '/') { + isc_buffer_putstr(&buf, "/"); + } + + if (isc_buffer_availablelength(&buf) < strlen(prefix)) { + fatal("%s: pathname too long", path); + } + isc_buffer_putstr(&buf, prefix); + + result = dns_name_tofilenametext(name, false, &buf); + check_result(result, "dns_name_tofilenametext()"); + if (isc_buffer_availablelength(&buf) == 0) { + fatal("%s: pathname too long", path); + } + } + /* allow room for a trailing nul */ + if (isc_buffer_availablelength(&buf) <= strlen(suffix)) { + fatal("%s: pathname too long", path); + } + isc_buffer_putstr(&buf, suffix); + isc_buffer_putuint8(&buf, 0); +} + +static void +load_parent_set(const char *path) { + isc_result_t result; + isc_time_t modtime; + char filename[PATH_MAX + 1]; + + get_dsset_name(filename, sizeof(filename), path, ""); + + result = isc_file_getmodtime(filename, &modtime); + if (result != ISC_R_SUCCESS) { + fatal("could not get modification time of %s: %s", filename, + isc_result_totext(result)); + } + notbefore = isc_time_seconds(&modtime); + if (startstr != NULL) { + isc_stdtime_t now; + isc_stdtime_get(&now); + notbefore = strtotime(startstr, now, notbefore, NULL); + } + verbose_time(1, "child records must not be signed before", notbefore); + + load_db(filename, &parent_db, &parent_node); + findset(parent_db, parent_node, dns_rdatatype_ds, &old_ds_set, NULL); + + if (!dns_rdataset_isassociated(&old_ds_set)) { + fatal("could not find DS records for %s in %s", namestr, + filename); + } + + free_db(&parent_db, &parent_node, NULL); +} + +#define MAX_CDS_RDATA_TEXT_SIZE DNS_RDATA_MAXLENGTH * 2 + +static isc_buffer_t * +formatset(dns_rdataset_t *rdataset) { + isc_result_t result; + isc_buffer_t *buf = NULL; + dns_master_style_t *style = NULL; + unsigned int styleflags; + + styleflags = (rdataset->ttl == 0) ? DNS_STYLEFLAG_NO_TTL : 0; + + /* + * This style is for consistency with the output of dnssec-dsfromkey + * which just separates fields with spaces. The huge tab stop width + * eliminates any tab characters. + */ + result = dns_master_stylecreate(&style, styleflags, 0, 0, 0, 0, 0, + 1000000, 0, mctx); + check_result(result, "dns_master_stylecreate2 failed"); + + isc_buffer_allocate(mctx, &buf, MAX_CDS_RDATA_TEXT_SIZE); + result = dns_master_rdatasettotext(name, rdataset, style, NULL, buf); + dns_master_styledestroy(&style, mctx); + + if ((result == ISC_R_SUCCESS) && isc_buffer_availablelength(buf) < 1) { + result = ISC_R_NOSPACE; + } + + if (result != ISC_R_SUCCESS) { + isc_buffer_free(&buf); + check_result(result, "dns_rdataset_totext()"); + } + + isc_buffer_putuint8(buf, 0); + return (buf); +} + +static void +write_parent_set(const char *path, const char *inplace, bool nsupdate, + dns_rdataset_t *rdataset) { + isc_result_t result; + isc_buffer_t *buf = NULL; + isc_region_t r; + isc_time_t filetime; + char backname[PATH_MAX + 1]; + char filename[PATH_MAX + 1]; + char tmpname[PATH_MAX + 1]; + FILE *fp = NULL; + + if (nsupdate && inplace == NULL) { + return; + } + + buf = formatset(rdataset); + isc_buffer_usedregion(buf, &r); + + /* + * Try to ensure a write error doesn't make a zone go insecure! + */ + if (inplace == NULL) { + printf("%s", (char *)r.base); + isc_buffer_free(&buf); + if (fflush(stdout) == EOF) { + fatal("error writing to stdout: %s", strerror(errno)); + } + return; + } + + if (inplace[0] != '\0') { + get_dsset_name(backname, sizeof(backname), path, inplace); + } + get_dsset_name(filename, sizeof(filename), path, ""); + get_dsset_name(tmpname, sizeof(tmpname), path, "-XXXXXXXXXX"); + + result = isc_file_openunique(tmpname, &fp); + if (result != ISC_R_SUCCESS) { + isc_buffer_free(&buf); + fatal("open %s: %s", tmpname, isc_result_totext(result)); + } + fprintf(fp, "%s", (char *)r.base); + isc_buffer_free(&buf); + if (fclose(fp) == EOF) { + int err = errno; + isc_file_remove(tmpname); + fatal("error writing to %s: %s", tmpname, strerror(err)); + } + + isc_time_set(&filetime, oldestsig.timesigned, 0); + result = isc_file_settime(tmpname, &filetime); + if (result != ISC_R_SUCCESS) { + isc_file_remove(tmpname); + fatal("can't set modification time of %s: %s", tmpname, + isc_result_totext(result)); + } + + if (inplace[0] != '\0') { + isc_file_rename(filename, backname); + } + isc_file_rename(tmpname, filename); +} + +typedef enum { LOOSE, TIGHT } strictness_t; + +/* + * Find out if any (C)DS record matches a particular (C)DNSKEY. + */ +static bool +match_key_dsset(keyinfo_t *ki, dns_rdataset_t *dsset, strictness_t strictness) { + isc_result_t result; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + + for (result = dns_rdataset_first(dsset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset)) + { + dns_rdata_ds_t ds; + dns_rdata_t dsrdata = DNS_RDATA_INIT; + dns_rdata_t newdsrdata = DNS_RDATA_INIT; + bool c; + + dns_rdataset_current(dsset, &dsrdata); + result = dns_rdata_tostruct(&dsrdata, &ds, NULL); + check_result(result, "dns_rdata_tostruct(DS)"); + + if (ki->tag != ds.key_tag || ki->algo != ds.algorithm) { + continue; + } + + result = dns_ds_buildrdata(name, &ki->rdata, ds.digest_type, + dsbuf, &newdsrdata); + if (result != ISC_R_SUCCESS) { + vbprintf(3, + "dns_ds_buildrdata(" + "keytag=%d, algo=%d, digest=%d): %s\n", + ds.key_tag, ds.algorithm, ds.digest_type, + isc_result_totext(result)); + continue; + } + /* allow for both DS and CDS */ + c = dsrdata.type != dns_rdatatype_ds; + dsrdata.type = dns_rdatatype_ds; + if (dns_rdata_compare(&dsrdata, &newdsrdata) == 0) { + vbprintf(1, "found matching %s %d %d %d\n", + c ? "CDS" : "DS", ds.key_tag, ds.algorithm, + ds.digest_type); + return (true); + } else if (strictness == TIGHT) { + vbprintf(0, + "key does not match %s %d %d %d " + "when it looks like it should\n", + c ? "CDS" : "DS", ds.key_tag, ds.algorithm, + ds.digest_type); + return (false); + } + } + + vbprintf(1, "no matching %s for %s %d %d\n", + dsset->type == dns_rdatatype_cds ? "CDS" : "DS", + ki->rdata.type == dns_rdatatype_cdnskey ? "CDNSKEY" : "DNSKEY", + ki->tag, ki->algo); + + return (false); +} + +/* + * Find which (C)DNSKEY records match a (C)DS RRset. + * This creates a keyinfo_t key_tbl[nkey] array. + */ +static keyinfo_t * +match_keyset_dsset(dns_rdataset_t *keyset, dns_rdataset_t *dsset, + strictness_t strictness) { + isc_result_t result; + keyinfo_t *keytable, *ki; + int i; + + nkey = dns_rdataset_count(keyset); + + keytable = isc_mem_get(mctx, sizeof(keyinfo_t) * nkey); + + for (result = dns_rdataset_first(keyset), i = 0, ki = keytable; + result == ISC_R_SUCCESS; + result = dns_rdataset_next(keyset), i++, ki++) + { + dns_rdata_dnskey_t dnskey; + dns_rdata_t *keyrdata; + isc_region_t r; + + INSIST(i < nkey); + keyrdata = &ki->rdata; + + dns_rdata_init(keyrdata); + dns_rdataset_current(keyset, keyrdata); + + result = dns_rdata_tostruct(keyrdata, &dnskey, NULL); + check_result(result, "dns_rdata_tostruct(DNSKEY)"); + ki->algo = dnskey.algorithm; + + dns_rdata_toregion(keyrdata, &r); + ki->tag = dst_region_computeid(&r); + + ki->dst = NULL; + if (!match_key_dsset(ki, dsset, strictness)) { + continue; + } + + result = dns_dnssec_keyfromrdata(name, keyrdata, mctx, + &ki->dst); + if (result != ISC_R_SUCCESS) { + vbprintf(3, + "dns_dnssec_keyfromrdata(" + "keytag=%d, algo=%d): %s\n", + ki->tag, ki->algo, isc_result_totext(result)); + } + } + + return (keytable); +} + +static void +free_keytable(keyinfo_t **keytable_p) { + keyinfo_t *keytable = *keytable_p; + *keytable_p = NULL; + keyinfo_t *ki; + int i; + + REQUIRE(keytable != NULL); + + for (i = 0, ki = keytable; i < nkey; i++, ki++) { + if (ki->dst != NULL) { + dst_key_free(&ki->dst); + } + } + + isc_mem_put(mctx, keytable, sizeof(keyinfo_t) * nkey); +} + +/* + * Find out which keys have signed an RRset. Keys that do not match a + * DS record are skipped. + * + * The return value is an array with nkey elements, one for each key, + * either zero if the key was skipped or did not sign the RRset, or + * otherwise the key algorithm. This is used by the signature coverage + * check functions below. + */ +static dns_secalg_t * +matching_sigs(keyinfo_t *keytbl, dns_rdataset_t *rdataset, + dns_rdataset_t *sigset) { + isc_result_t result; + dns_secalg_t *algo; + int i; + + REQUIRE(keytbl != NULL); + + algo = isc_mem_get(mctx, nkey); + memset(algo, 0, nkey); + + for (result = dns_rdataset_first(sigset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(sigset)) + { + dns_rdata_t sigrdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t sig; + + dns_rdataset_current(sigset, &sigrdata); + result = dns_rdata_tostruct(&sigrdata, &sig, NULL); + check_result(result, "dns_rdata_tostruct(RRSIG)"); + + /* + * Replay attack protection: check against current age limit + */ + if (isc_serial_lt(sig.timesigned, notbefore)) { + vbprintf(1, "skip RRSIG by key %d: too old\n", + sig.keyid); + continue; + } + + for (i = 0; i < nkey; i++) { + keyinfo_t *ki = &keytbl[i]; + if (sig.keyid != ki->tag || sig.algorithm != ki->algo || + !dns_name_equal(&sig.signer, name)) + { + continue; + } + if (ki->dst == NULL) { + vbprintf(1, + "skip RRSIG by key %d:" + " no matching (C)DS\n", + sig.keyid); + continue; + } + + result = dns_dnssec_verify(name, rdataset, ki->dst, + false, 0, mctx, &sigrdata, + NULL); + + if (result != ISC_R_SUCCESS && + result != DNS_R_FROMWILDCARD) + { + vbprintf(1, + "skip RRSIG by key %d:" + " verification failed: %s\n", + sig.keyid, isc_result_totext(result)); + continue; + } + + vbprintf(1, "found RRSIG by key %d\n", ki->tag); + algo[i] = sig.algorithm; + + /* + * Replay attack protection: work out next age limit, + * only after the signature has been verified + */ + if (oldestsig.timesigned == 0 || + isc_serial_lt(sig.timesigned, oldestsig.timesigned)) + { + verbose_time(2, "this is the oldest so far", + sig.timesigned); + oldestsig = sig; + } + } + } + + return (algo); +} + +/* + * Consume the result of matching_sigs(). When checking records + * fetched from the child zone, any working signature is enough. + */ +static bool +signed_loose(dns_secalg_t *algo) { + bool ok = false; + int i; + for (i = 0; i < nkey; i++) { + if (algo[i] != 0) { + ok = true; + } + } + isc_mem_put(mctx, algo, nkey); + return (ok); +} + +/* + * Consume the result of matching_sigs(). To ensure that the new DS + * RRset does not break the chain of trust to the DNSKEY RRset, every + * key algorithm in the DS RRset must have a signature in the DNSKEY + * RRset. + */ +static bool +signed_strict(dns_rdataset_t *dsset, dns_secalg_t *algo) { + isc_result_t result; + bool all_ok = true; + + for (result = dns_rdataset_first(dsset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset)) + { + dns_rdata_t dsrdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + bool ds_ok; + int i; + + dns_rdataset_current(dsset, &dsrdata); + result = dns_rdata_tostruct(&dsrdata, &ds, NULL); + check_result(result, "dns_rdata_tostruct(DS)"); + + ds_ok = false; + for (i = 0; i < nkey; i++) { + if (algo[i] == ds.algorithm) { + ds_ok = true; + } + } + if (!ds_ok) { + vbprintf(0, + "missing signature for algorithm %d " + "(key %d)\n", + ds.algorithm, ds.key_tag); + all_ok = false; + } + } + + isc_mem_put(mctx, algo, nkey); + return (all_ok); +} + +/* + * This basically copies the rdata into the buffer, but going via the + * unpacked struct lets us change the rdatatype. (The dns_rdata_cds_t + * and dns_rdata_ds_t types are aliases.) + */ +static isc_result_t +ds_from_cds(isc_buffer_t *buf, dns_rdata_t *rds, dns_dsdigest_t dt, + dns_rdata_t *cds) { + isc_result_t result; + dns_rdata_ds_t ds; + + REQUIRE(buf != NULL); + + result = dns_rdata_tostruct(cds, &ds, NULL); + check_result(result, "dns_rdata_tostruct(CDS)"); + ds.common.rdtype = dns_rdatatype_ds; + + if (ds.digest_type != dt) { + return (ISC_R_IGNORE); + } + + return (dns_rdata_fromstruct(rds, rdclass, dns_rdatatype_ds, &ds, buf)); +} + +static isc_result_t +ds_from_cdnskey(isc_buffer_t *buf, dns_rdata_t *ds, dns_dsdigest_t dt, + dns_rdata_t *cdnskey) { + isc_result_t result; + isc_region_t r; + + REQUIRE(buf != NULL); + + isc_buffer_availableregion(buf, &r); + if (r.length < DNS_DS_BUFFERSIZE) { + return (ISC_R_NOSPACE); + } + + result = dns_ds_buildrdata(name, cdnskey, dt, r.base, ds); + if (result == ISC_R_SUCCESS) { + isc_buffer_add(buf, DNS_DS_BUFFERSIZE); + } + + return (result); +} + +static isc_result_t +append_new_ds_set(ds_maker_func_t *ds_from_rdata, isc_buffer_t *buf, + dns_rdatalist_t *dslist, dns_dsdigest_t dt, + dns_rdataset_t *crdset) { + isc_result_t result; + + for (result = dns_rdataset_first(crdset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(crdset)) + { + dns_rdata_t crdata = DNS_RDATA_INIT; + dns_rdata_t *ds = NULL; + + dns_rdataset_current(crdset, &crdata); + + ds = isc_mem_get(mctx, sizeof(*ds)); + dns_rdata_init(ds); + + result = ds_from_rdata(buf, ds, dt, &crdata); + + switch (result) { + case ISC_R_SUCCESS: + ISC_LIST_APPEND(dslist->rdata, ds, link); + break; + case ISC_R_IGNORE: + isc_mem_put(mctx, ds, sizeof(*ds)); + continue; + case ISC_R_NOSPACE: + isc_mem_put(mctx, ds, sizeof(*ds)); + return (result); + default: + isc_mem_put(mctx, ds, sizeof(*ds)); + check_result(result, "ds_from_rdata()"); + } + } + + return (ISC_R_SUCCESS); +} + +static void +make_new_ds_set(ds_maker_func_t *ds_from_rdata, uint32_t ttl, + dns_rdataset_t *crdset) { + isc_result_t result; + dns_rdatalist_t *dslist; + unsigned int size = 16; + unsigned i, n; + + for (;;) { + dslist = isc_mem_get(mctx, sizeof(*dslist)); + dns_rdatalist_init(dslist); + dslist->rdclass = rdclass; + dslist->type = dns_rdatatype_ds; + dslist->ttl = ttl; + + dns_rdataset_init(&new_ds_set); + result = dns_rdatalist_tordataset(dslist, &new_ds_set); + check_result(result, "dns_rdatalist_tordataset(dslist)"); + + isc_buffer_allocate(mctx, &new_ds_buf, size); + + n = sizeof(dtype) / sizeof(dtype[0]); + for (i = 0; i < n && dtype[i] != 0; i++) { + result = append_new_ds_set(ds_from_rdata, new_ds_buf, + dslist, dtype[i], crdset); + if (result != ISC_R_SUCCESS) { + break; + } + } + if (result == ISC_R_SUCCESS) { + return; + } + + vbprintf(2, "doubling DS list buffer size from %u\n", size); + freelist(&new_ds_set); + isc_buffer_free(&new_ds_buf); + size *= 2; + } +} + +static int +rdata_cmp(const void *rdata1, const void *rdata2) { + return (dns_rdata_compare((const dns_rdata_t *)rdata1, + (const dns_rdata_t *)rdata2)); +} + +/* + * Ensure that every key identified by the DS RRset has the same set of + * digest types. + */ +static bool +consistent_digests(dns_rdataset_t *dsset) { + isc_result_t result; + dns_rdata_t *arrdata; + dns_rdata_ds_t *ds; + dns_keytag_t key_tag; + dns_secalg_t algorithm; + bool match; + int i, j, n, d; + + /* + * First sort the dsset. DS rdata fields are tag, algorithm, + * digest, so sorting them brings together all the records for + * each key. + */ + + n = dns_rdataset_count(dsset); + + arrdata = isc_mem_get(mctx, n * sizeof(dns_rdata_t)); + + for (result = dns_rdataset_first(dsset), i = 0; result == ISC_R_SUCCESS; + result = dns_rdataset_next(dsset), i++) + { + dns_rdata_init(&arrdata[i]); + dns_rdataset_current(dsset, &arrdata[i]); + } + + qsort(arrdata, n, sizeof(dns_rdata_t), rdata_cmp); + + /* + * Convert sorted arrdata to more accessible format + */ + ds = isc_mem_get(mctx, n * sizeof(dns_rdata_ds_t)); + + for (i = 0; i < n; i++) { + result = dns_rdata_tostruct(&arrdata[i], &ds[i], NULL); + check_result(result, "dns_rdata_tostruct(DS)"); + } + + /* + * Count number of digest types (d) for first key + */ + key_tag = ds[0].key_tag; + algorithm = ds[0].algorithm; + for (d = 0, i = 0; i < n; i++, d++) { + if (ds[i].key_tag != key_tag || ds[i].algorithm != algorithm) { + break; + } + } + + /* + * Check subsequent keys match the first one + */ + match = true; + while (i < n) { + key_tag = ds[i].key_tag; + algorithm = ds[i].algorithm; + for (j = 0; j < d && i + j < n; j++) { + if (ds[i + j].key_tag != key_tag || + ds[i + j].algorithm != algorithm || + ds[i + j].digest_type != ds[j].digest_type) + { + match = false; + } + } + i += d; + } + + /* + * Done! + */ + isc_mem_put(mctx, ds, n * sizeof(dns_rdata_ds_t)); + isc_mem_put(mctx, arrdata, n * sizeof(dns_rdata_t)); + + return (match); +} + +static void +print_diff(const char *cmd, dns_rdataset_t *rdataset) { + isc_buffer_t *buf; + isc_region_t r; + unsigned char *nl; + size_t len; + + buf = formatset(rdataset); + isc_buffer_usedregion(buf, &r); + + while ((nl = memchr(r.base, '\n', r.length)) != NULL) { + len = nl - r.base + 1; + printf("update %s %.*s", cmd, (int)len, (char *)r.base); + isc_region_consume(&r, len); + } + + isc_buffer_free(&buf); +} + +static void +update_diff(const char *cmd, uint32_t ttl, dns_rdataset_t *addset, + dns_rdataset_t *delset) { + isc_result_t result; + dns_rdataset_t diffset; + uint32_t save; + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, &update_db); + check_result(result, "dns_db_create()"); + + result = dns_db_newversion(update_db, &update_version); + check_result(result, "dns_db_newversion()"); + + result = dns_db_findnode(update_db, name, true, &update_node); + check_result(result, "dns_db_findnode()"); + + dns_rdataset_init(&diffset); + + result = dns_db_addrdataset(update_db, update_node, update_version, 0, + addset, DNS_DBADD_MERGE, NULL); + check_result(result, "dns_db_addrdataset()"); + + result = dns_db_subtractrdataset(update_db, update_node, update_version, + delset, 0, &diffset); + if (result == DNS_R_UNCHANGED) { + save = addset->ttl; + addset->ttl = ttl; + print_diff(cmd, addset); + addset->ttl = save; + } else if (result != DNS_R_NXRRSET) { + check_result(result, "dns_db_subtractrdataset()"); + diffset.ttl = ttl; + print_diff(cmd, &diffset); + dns_rdataset_disassociate(&diffset); + } + + free_db(&update_db, &update_node, &update_version); +} + +static void +nsdiff(uint32_t ttl, dns_rdataset_t *oldset, dns_rdataset_t *newset) { + if (ttl == 0) { + vbprintf(1, "warning: no TTL in nsupdate script\n"); + } + update_diff("add", ttl, newset, oldset); + update_diff("del", 0, oldset, newset); + if (verbose > 0) { + printf("show\nsend\nanswer\n"); + } else { + printf("send\n"); + } + if (fflush(stdout) == EOF) { + fatal("write stdout: %s", strerror(errno)); + } +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, + " %s options [options] -f -d \n", + program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "Options:\n" + " -a digest algorithm (SHA-1 / " + "SHA-256 / SHA-384)\n" + " -c of domain (default IN)\n" + " -D prefer CDNSKEY records instead " + "of CDS\n" + " -d where to find parent dsset- " + "file\n" + " -f child DNSKEY+CDNSKEY+CDS+RRSIG " + "records\n" + " -i[extension] update dsset- file in place\n" + " -s oldest permitted child " + "signatures\n" + " -u emit nsupdate script\n" + " -T TTL of DS records\n" + " -V print version\n" + " -v \n"); + exit(1); +} + +static void +cleanup(void) { + free_db(&child_db, &child_node, NULL); + free_db(&parent_db, &parent_node, NULL); + free_db(&update_db, &update_node, &update_version); + if (old_key_tbl != NULL) { + free_keytable(&old_key_tbl); + } + if (new_key_tbl != NULL) { + free_keytable(&new_key_tbl); + } + free_all_sets(); + if (lctx != NULL) { + cleanup_logging(&lctx); + } + if (cleanup_dst) { + dst_lib_destroy(); + } + if (mctx != NULL) { + if (print_mem_stats && verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + } +} + +int +main(int argc, char *argv[]) { + const char *child_path = NULL; + const char *ds_path = NULL; + const char *inplace = NULL; + isc_result_t result; + bool prefer_cdnskey = false; + bool nsupdate = false; + uint32_t ttl = 0; + int ch; + char *endp; + + setfatalcallback(cleanup); + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + +#define OPTIONS "a:c:Dd:f:i:ms:T:uv:V" + while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { + switch (ch) { + case 'a': + add_dtype(strtodsdigest(isc_commandline_argument)); + break; + case 'c': + rdclass = strtoclass(isc_commandline_argument); + break; + case 'D': + prefer_cdnskey = true; + break; + case 'd': + ds_path = isc_commandline_argument; + break; + case 'f': + child_path = isc_commandline_argument; + break; + case 'i': + /* + * This is a bodge to make the argument + * optional, so that it works just like sed(1). + */ + if (isc_commandline_argument == + argv[isc_commandline_index - 1]) + { + isc_commandline_index--; + inplace = ""; + } else { + inplace = isc_commandline_argument; + } + break; + case 'm': + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + case 's': + startstr = isc_commandline_argument; + break; + case 'T': + ttl = strtottl(isc_commandline_argument); + break; + case 'u': + nsupdate = true; + break; + case 'V': + /* Does not return. */ + version(program); + break; + case 'v': + verbose = strtoul(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + default: + usage(); + break; + } + } + argv += isc_commandline_index; + argc -= isc_commandline_index; + + if (argc != 1) { + usage(); + } + initname(argv[0]); + + /* + * Default digest type if none specified. + */ + if (dtype[0] == 0) { + dtype[0] = DNS_DSDIGEST_SHA256; + } + + setup_logging(mctx, &lctx); + + result = dst_lib_init(mctx, NULL); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", + isc_result_totext(result)); + } + cleanup_dst = true; + + if (ds_path == NULL) { + fatal("missing -d DS pathname"); + } + load_parent_set(ds_path); + + /* + * Preserve the TTL if it wasn't overridden. + */ + if (ttl == 0) { + ttl = old_ds_set.ttl; + } + + if (child_path == NULL) { + fatal("path to file containing child data must be specified"); + } + + load_child_sets(child_path); + + /* + * Check child records have accompanying RRSIGs and DNSKEYs + */ + + if (!dns_rdataset_isassociated(&dnskey_set) || + !dns_rdataset_isassociated(&dnskey_sig)) + { + fatal("could not find signed DNSKEY RRset for %s", namestr); + } + + if (dns_rdataset_isassociated(&cdnskey_set) && + !dns_rdataset_isassociated(&cdnskey_sig)) + { + fatal("missing RRSIG CDNSKEY records for %s", namestr); + } + if (dns_rdataset_isassociated(&cds_set) && + !dns_rdataset_isassociated(&cds_sig)) + { + fatal("missing RRSIG CDS records for %s", namestr); + } + + vbprintf(1, "which child DNSKEY records match parent DS records?\n"); + old_key_tbl = match_keyset_dsset(&dnskey_set, &old_ds_set, LOOSE); + + /* + * We have now identified the keys that are allowed to + * authenticate the DNSKEY RRset (RFC 4035 section 5.2 bullet + * 2), and CDNSKEY and CDS RRsets (RFC 7344 section 4.1 bullet + * 2). + */ + + vbprintf(1, "verify DNSKEY signature(s)\n"); + if (!signed_loose(matching_sigs(old_key_tbl, &dnskey_set, &dnskey_sig))) + { + fatal("could not validate child DNSKEY RRset for %s", namestr); + } + + if (dns_rdataset_isassociated(&cdnskey_set)) { + vbprintf(1, "verify CDNSKEY signature(s)\n"); + if (!signed_loose(matching_sigs(old_key_tbl, &cdnskey_set, + &cdnskey_sig))) + { + fatal("could not validate child CDNSKEY RRset for %s", + namestr); + } + } + if (dns_rdataset_isassociated(&cds_set)) { + vbprintf(1, "verify CDS signature(s)\n"); + if (!signed_loose( + matching_sigs(old_key_tbl, &cds_set, &cds_sig))) + { + fatal("could not validate child CDS RRset for %s", + namestr); + } + } + + free_keytable(&old_key_tbl); + + /* + * Report the result of the replay attack protection checks + * used for the output file timestamp + */ + if (oldestsig.timesigned != 0 && verbose > 0) { + char type[32]; + dns_rdatatype_format(oldestsig.covered, type, sizeof(type)); + verbose_time(1, "child signature inception time", + oldestsig.timesigned); + vbprintf(2, "from RRSIG %s by key %d\n", type, oldestsig.keyid); + } + + /* + * Successfully do nothing if there's neither CDNSKEY nor CDS + * RFC 7344 section 4.1 first paragraph + */ + if (!dns_rdataset_isassociated(&cdnskey_set) && + !dns_rdataset_isassociated(&cds_set)) + { + vbprintf(1, "%s has neither CDS nor CDNSKEY records\n", + namestr); + write_parent_set(ds_path, inplace, nsupdate, &old_ds_set); + goto cleanup; + } + + /* + * Make DS records from the CDS or CDNSKEY records + * Prefer CDS if present, unless run with -D + */ + if (prefer_cdnskey && dns_rdataset_isassociated(&cdnskey_set)) { + make_new_ds_set(ds_from_cdnskey, ttl, &cdnskey_set); + } else if (dns_rdataset_isassociated(&cds_set)) { + make_new_ds_set(ds_from_cds, ttl, &cds_set); + } else { + make_new_ds_set(ds_from_cdnskey, ttl, &cdnskey_set); + } + + /* + * Try to use CDNSKEY records if the CDS records are missing + * or did not match. + */ + if (dns_rdataset_count(&new_ds_set) == 0 && + dns_rdataset_isassociated(&cdnskey_set)) + { + vbprintf(1, "CDS records have no allowed digest types; " + "using CDNSKEY instead\n"); + freelist(&new_ds_set); + isc_buffer_free(&new_ds_buf); + make_new_ds_set(ds_from_cdnskey, ttl, &cdnskey_set); + } + if (dns_rdataset_count(&new_ds_set) == 0) { + fatal("CDS records at %s do not match any -a digest types", + namestr); + } + + /* + * Now we have a candidate DS RRset, we need to check it + * won't break the delegation. + */ + vbprintf(1, "which child DNSKEY records match new DS records?\n"); + new_key_tbl = match_keyset_dsset(&dnskey_set, &new_ds_set, TIGHT); + + if (!consistent_digests(&new_ds_set)) { + fatal("CDS records at %s do not cover each key " + "with the same set of digest types", + namestr); + } + + vbprintf(1, "verify DNSKEY signature(s)\n"); + if (!signed_strict(&new_ds_set, matching_sigs(new_key_tbl, &dnskey_set, + &dnskey_sig))) + { + fatal("could not validate child DNSKEY RRset " + "with new DS records for %s", + namestr); + } + + free_keytable(&new_key_tbl); + + /* + * OK, it's all good! + */ + if (nsupdate) { + nsdiff(ttl, &old_ds_set, &new_ds_set); + } + + write_parent_set(ds_path, inplace, nsupdate, &new_ds_set); + +cleanup: + print_mem_stats = true; + cleanup(); + exit(0); +} diff --git a/bin/dnssec/dnssec-cds.rst b/bin/dnssec/dnssec-cds.rst new file mode 100644 index 0000000..09960e9 --- /dev/null +++ b/bin/dnssec/dnssec-cds.rst @@ -0,0 +1,221 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-cds +.. program:: dnssec-cds +.. _man_dnssec-cds: + +dnssec-cds - change DS records for a child zone based on CDS/CDNSKEY +-------------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-cds` [**-a** alg...] [**-c** class] [**-D**] {**-d** dsset-file} {**-f** child-file} [**-i**[extension]] [**-s** start-time] [**-T** ttl] [**-u**] [**-v** level] [**-V**] {domain} + +Description +~~~~~~~~~~~ + +The :program:`dnssec-cds` command changes DS records at a delegation point +based on CDS or CDNSKEY records published in the child zone. If both CDS +and CDNSKEY records are present in the child zone, the CDS is preferred. +This enables a child zone to inform its parent of upcoming changes to +its key-signing keys (KSKs); by polling periodically with :program:`dnssec-cds`, the +parent can keep the DS records up-to-date and enable automatic rolling +of KSKs. + +Two input files are required. The :option:`-f child-file <-f>` option specifies a +file containing the child's CDS and/or CDNSKEY records, plus RRSIG and +DNSKEY records so that they can be authenticated. The :option:`-d path <-d>` option +specifies the location of a file containing the current DS records. For +example, this could be a ``dsset-`` file generated by +:iscman:`dnssec-signzone`, or the output of :iscman:`dnssec-dsfromkey`, or the +output of a previous run of :program:`dnssec-cds`. + +The :program:`dnssec-cds` command uses special DNSSEC validation logic +specified by :rfc:`7344`. It requires that the CDS and/or CDNSKEY records +be validly signed by a key represented in the existing DS records. This +is typically the pre-existing KSK. + +For protection against replay attacks, the signatures on the child +records must not be older than they were on a previous run of +:program:`dnssec-cds`. Their age is obtained from the modification time of the +``dsset-`` file, or from the :option:`-s` option. + +To protect against breaking the delegation, :program:`dnssec-cds` ensures that +the DNSKEY RRset can be verified by every key algorithm in the new DS +RRset, and that the same set of keys are covered by every DS digest +type. + +By default, replacement DS records are written to the standard output; +with the :option:`-i` option the input file is overwritten in place. The +replacement DS records are the same as the existing records, when no +change is required. The output can be empty if the CDS/CDNSKEY records +specify that the child zone wants to be insecure. + +.. warning:: + + Be careful not to delete the DS records when :program:`dnssec-cds` fails! + +Alternatively, :option`dnssec-cds -u` writes an :iscman:`nsupdate` script to the +standard output. The :option:`-u` and :option:`-i` options can be used together to +maintain a ``dsset-`` file as well as emit an :iscman:`nsupdate` script. + +Options +~~~~~~~ + +.. option:: -a algorithm + + When converting CDS records to DS records, this option specifies + the acceptable digest algorithms. This option can be repeated, so + that multiple digest types are allowed. If none of the CDS records + use an acceptable digest type, :program:`dnssec-cds` will try to use CDNSKEY + records instead; if there are no CDNSKEY records, it reports an error. + + When converting CDNSKEY records to DS records, this option specifies the + digest algorithm to use. It can be repeated, so that multiple DS records + are created for each CDNSKEY records. + + The algorithm must be one of SHA-1, SHA-256, or SHA-384. These values + are case-insensitive, and the hyphen may be omitted. If no algorithm + is specified, the default is SHA-256 only. + +.. option:: -c class + + This option specifies the DNS class of the zones. + +.. option:: -D + + This option generates DS records from CDNSKEY records if both CDS and CDNSKEY + records are present in the child zone. By default CDS records are + preferred. + +.. option:: -d path + + This specifies the location of the parent DS records. The path can be the name of a file + containing the DS records; if it is a directory, :program:`dnssec-cds` + looks for a ``dsset-`` file for the domain inside the directory. + + To protect against replay attacks, child records are rejected if they + were signed earlier than the modification time of the ``dsset-`` + file. This can be adjusted with the :option:`-s` option. + +.. option:: -f child-file + + This option specifies the file containing the child's CDS and/or CDNSKEY records, plus its + DNSKEY records and the covering RRSIG records, so that they can be + authenticated. + + The examples below describe how to generate this file. + +.. option:: -i extension + + This option updates the ``dsset-`` file in place, instead of writing DS records to + the standard output. + + There must be no space between the :option:`-i` and the extension. If + no extension is provided, the old ``dsset-`` is discarded. If an + extension is present, a backup of the old ``dsset-`` file is kept + with the extension appended to its filename. + + To protect against replay attacks, the modification time of the + ``dsset-`` file is set to match the signature inception time of the + child records, provided that it is later than the file's current + modification time. + +.. option:: -s start-time + + This option specifies the date and time after which RRSIG records become + acceptable. This can be either an absolute or a relative time. An + absolute start time is indicated by a number in YYYYMMDDHHMMSS + notation; 20170827133700 denotes 13:37:00 UTC on August 27th, 2017. A + time relative to the ``dsset-`` file is indicated with ``-N``, which is N + seconds before the file modification time. A time relative to the + current time is indicated with ``now+N``. + + If no start-time is specified, the modification time of the + ``dsset-`` file is used. + +.. option:: -T ttl + + This option specifies a TTL to be used for new DS records. If not specified, the + default is the TTL of the old DS records. If they had no explicit TTL, + the new DS records also have no explicit TTL. + +.. option:: -u + + This option writes an :iscman:`nsupdate` script to the standard output, instead of + printing the new DS reords. The output is empty if no change is + needed. + + Note: The TTL of new records needs to be specified: it can be done in the + original ``dsset-`` file, with the :option:`-T` option, or using the + :iscman:`nsupdate` ``ttl`` command. + +.. option:: -V + + This option prints version information. + +.. option:: -v level + + This option sets the debugging level. Level 1 is intended to be usefully verbose + for general users; higher levels are intended for developers. + +``domain`` + This indicates the name of the delegation point/child zone apex. + +Exit Status +~~~~~~~~~~~ + +The :program:`dnssec-cds` command exits 0 on success, or non-zero if an error +occurred. + +If successful, the DS records may or may not need to be +changed. + +Examples +~~~~~~~~ + +Before running :iscman:`dnssec-signzone`, ensure that the delegations +are up-to-date by running :program:`dnssec-cds` on every ``dsset-`` file. + +To fetch the child records required by :program:`dnssec-cds`, invoke +:iscman:`dig` as in the script below. It is acceptable if the :iscman:`dig` fails, since +:program:`dnssec-cds` performs all the necessary checking. + +:: + + for f in dsset-* + do + d=${f#dsset-} + dig +dnssec +noall +answer $d DNSKEY $d CDNSKEY $d CDS | + dnssec-cds -i -f /dev/stdin -d $f $d + done + +When the parent zone is automatically signed by :iscman:`named`, +:program:`dnssec-cds` can be used with :iscman:`nsupdate` to maintain a delegation as follows. +The ``dsset-`` file allows the script to avoid having to fetch and +validate the parent DS records, and it maintains the replay attack +protection time. + +:: + + dig +dnssec +noall +answer $d DNSKEY $d CDNSKEY $d CDS | + dnssec-cds -u -i -f /dev/stdin -d $f $d | + nsupdate -l + +See Also +~~~~~~~~ + +:iscman:`dig(1) `, :iscman:`dnssec-settime(8) `, :iscman:`dnssec-signzone(8) `, :iscman:`nsupdate(1) `, BIND 9 Administrator +Reference Manual, :rfc:`7344`. diff --git a/bin/dnssec/dnssec-dsfromkey.c b/bin/dnssec/dnssec-dsfromkey.c new file mode 100644 index 0000000..42aa9e5 --- /dev/null +++ b/bin/dnssec/dnssec-dsfromkey.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-dsfromkey"; + +static dns_rdataclass_t rdclass; +static dns_fixedname_t fixed; +static dns_name_t *name = NULL; +static isc_mem_t *mctx = NULL; +static uint32_t ttl; +static bool emitttl = false; + +static isc_result_t +initname(char *setname) { + isc_result_t result; + isc_buffer_t buf; + + name = dns_fixedname_initname(&fixed); + + isc_buffer_init(&buf, setname, strlen(setname)); + isc_buffer_add(&buf, strlen(setname)); + result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + return (result); +} + +static void +db_load_from_stream(dns_db_t *db, FILE *fp) { + isc_result_t result; + dns_rdatacallbacks_t callbacks; + + dns_rdatacallbacks_init(&callbacks); + result = dns_db_beginload(db, &callbacks); + if (result != ISC_R_SUCCESS) { + fatal("dns_db_beginload failed: %s", isc_result_totext(result)); + } + + result = dns_master_loadstream(fp, name, name, rdclass, 0, &callbacks, + mctx); + if (result != ISC_R_SUCCESS) { + fatal("can't load from input: %s", isc_result_totext(result)); + } + + result = dns_db_endload(db, &callbacks); + if (result != ISC_R_SUCCESS) { + fatal("dns_db_endload failed: %s", isc_result_totext(result)); + } +} + +static isc_result_t +loadset(const char *filename, dns_rdataset_t *rdataset) { + isc_result_t result; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + char setname[DNS_NAME_FORMATSIZE]; + + dns_name_format(name, setname, sizeof(setname)); + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, &db); + if (result != ISC_R_SUCCESS) { + fatal("can't create database"); + } + + if (strcmp(filename, "-") == 0) { + db_load_from_stream(db, stdin); + filename = "input"; + } else { + result = dns_db_load(db, filename, dns_masterformat_text, 0); + if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { + fatal("can't load %s: %s", filename, + isc_result_totext(result)); + } + } + + result = dns_db_findnode(db, name, false, &node); + if (result != ISC_R_SUCCESS) { + fatal("can't find %s node in %s", setname, filename); + } + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0, + rdataset, NULL); + + if (result == ISC_R_NOTFOUND) { + fatal("no DNSKEY RR for %s in %s", setname, filename); + } else if (result != ISC_R_SUCCESS) { + fatal("dns_db_findrdataset"); + } + + if (node != NULL) { + dns_db_detachnode(db, &node); + } + if (db != NULL) { + dns_db_detach(&db); + } + return (result); +} + +static isc_result_t +loadkeyset(char *dirname, dns_rdataset_t *rdataset) { + isc_result_t result; + char filename[PATH_MAX + 1]; + isc_buffer_t buf; + + dns_rdataset_init(rdataset); + + isc_buffer_init(&buf, filename, sizeof(filename)); + if (dirname != NULL) { + /* allow room for a trailing slash */ + if (strlen(dirname) >= isc_buffer_availablelength(&buf)) { + return (ISC_R_NOSPACE); + } + isc_buffer_putstr(&buf, dirname); + if (dirname[strlen(dirname) - 1] != '/') { + isc_buffer_putstr(&buf, "/"); + } + } + + if (isc_buffer_availablelength(&buf) < 7) { + return (ISC_R_NOSPACE); + } + isc_buffer_putstr(&buf, "keyset-"); + + result = dns_name_tofilenametext(name, false, &buf); + check_result(result, "dns_name_tofilenametext()"); + if (isc_buffer_availablelength(&buf) == 0) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(&buf, 0); + + return (loadset(filename, rdataset)); +} + +static void +loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size, + dns_rdata_t *rdata) { + isc_result_t result; + dst_key_t *key = NULL; + isc_buffer_t keyb; + isc_region_t r; + + dns_rdata_init(rdata); + + isc_buffer_init(&keyb, key_buf, key_buf_size); + + result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, mctx, + &key); + if (result != ISC_R_SUCCESS) { + fatal("can't load %s.key: %s", filename, + isc_result_totext(result)); + } + + if (verbose > 2) { + char keystr[DST_KEY_FORMATSIZE]; + + dst_key_format(key, keystr, sizeof(keystr)); + fprintf(stderr, "%s: %s\n", program, keystr); + } + + result = dst_key_todns(key, &keyb); + if (result != ISC_R_SUCCESS) { + fatal("can't decode key"); + } + + isc_buffer_usedregion(&keyb, &r); + dns_rdata_fromregion(rdata, dst_key_class(key), dns_rdatatype_dnskey, + &r); + + rdclass = dst_key_class(key); + + name = dns_fixedname_initname(&fixed); + dns_name_copy(dst_key_name(key), name); + + dst_key_free(&key); +} + +static void +logkey(dns_rdata_t *rdata) { + isc_result_t result; + dst_key_t *key = NULL; + isc_buffer_t buf; + char keystr[DST_KEY_FORMATSIZE]; + + isc_buffer_init(&buf, rdata->data, rdata->length); + isc_buffer_add(&buf, rdata->length); + result = dst_key_fromdns(name, rdclass, &buf, mctx, &key); + if (result != ISC_R_SUCCESS) { + return; + } + + dst_key_format(key, keystr, sizeof(keystr)); + fprintf(stderr, "%s: %s\n", program, keystr); + + dst_key_free(&key); +} + +static void +emit(dns_dsdigest_t dt, bool showall, bool cds, dns_rdata_t *rdata) { + isc_result_t result; + unsigned char buf[DNS_DS_BUFFERSIZE]; + char text_buf[DST_KEY_MAXTEXTSIZE]; + char name_buf[DNS_NAME_MAXWIRE]; + char class_buf[10]; + isc_buffer_t textb, nameb, classb; + isc_region_t r; + dns_rdata_t ds; + dns_rdata_dnskey_t dnskey; + + isc_buffer_init(&textb, text_buf, sizeof(text_buf)); + isc_buffer_init(&nameb, name_buf, sizeof(name_buf)); + isc_buffer_init(&classb, class_buf, sizeof(class_buf)); + + dns_rdata_init(&ds); + + result = dns_rdata_tostruct(rdata, &dnskey, NULL); + if (result != ISC_R_SUCCESS) { + fatal("can't convert DNSKEY"); + } + + if ((dnskey.flags & DNS_KEYFLAG_REVOKE) != 0) { + return; + } + + if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0 && !showall) { + return; + } + + result = dns_ds_buildrdata(name, rdata, dt, buf, &ds); + if (result != ISC_R_SUCCESS) { + fatal("can't build record"); + } + + result = dns_name_totext(name, false, &nameb); + if (result != ISC_R_SUCCESS) { + fatal("can't print name"); + } + + result = dns_rdata_tofmttext(&ds, (dns_name_t *)NULL, 0, 0, 0, "", + &textb); + + if (result != ISC_R_SUCCESS) { + fatal("can't print rdata"); + } + + result = dns_rdataclass_totext(rdclass, &classb); + if (result != ISC_R_SUCCESS) { + fatal("can't print class"); + } + + isc_buffer_usedregion(&nameb, &r); + printf("%.*s ", (int)r.length, r.base); + + if (emitttl) { + printf("%u ", ttl); + } + + isc_buffer_usedregion(&classb, &r); + printf("%.*s", (int)r.length, r.base); + + if (cds) { + printf(" CDS "); + } else { + printf(" DS "); + } + + isc_buffer_usedregion(&textb, &r); + printf("%.*s\n", (int)r.length, r.base); +} + +static void +emits(bool showall, bool cds, dns_rdata_t *rdata) { + unsigned i, n; + + n = sizeof(dtype) / sizeof(dtype[0]); + for (i = 0; i < n; i++) { + if (dtype[i] != 0) { + emit(dtype[i], showall, cds, rdata); + } + } +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options] keyfile\n\n", program); + fprintf(stderr, " %s [options] -f zonefile [zonename]\n\n", program); + fprintf(stderr, " %s [options] -s dnsname\n\n", program); + fprintf(stderr, " %s [-h|-V]\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "Options:\n" + " -1: digest algorithm SHA-1\n" + " -2: digest algorithm SHA-256\n" + " -a algorithm: digest algorithm (SHA-1, SHA-256 or " + "SHA-384)\n" + " -A: include all keys in DS set, not just KSKs (-f " + "only)\n" + " -c class: rdata class for DS set (default IN) (-f " + "or -s only)\n" + " -C: print CDS records\n" + " -f zonefile: read keys from a zone file\n" + " -h: print help information\n" + " -K directory: where to find key or keyset files\n" + " -s: read keys from keyset- file\n" + " -T: TTL of output records (omitted by default)\n" + " -v level: verbosity\n" + " -V: print version information\n"); + fprintf(stderr, "Output: DS or CDS RRs\n"); + + exit(-1); +} + +int +main(int argc, char **argv) { + char *classname = NULL; + char *filename = NULL, *dir = NULL, *namestr; + char *endp, *arg1; + int ch; + bool cds = false; + bool usekeyset = false; + bool showall = false; + isc_result_t result; + isc_log_t *log = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata; + + dns_rdata_init(&rdata); + + if (argc == 1) { + usage(); + } + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + +#define OPTIONS "12Aa:Cc:d:Ff:K:l:sT:v:hV" + while ((ch = isc_commandline_parse(argc, argv, OPTIONS)) != -1) { + switch (ch) { + case '1': + add_dtype(DNS_DSDIGEST_SHA1); + break; + case '2': + add_dtype(DNS_DSDIGEST_SHA256); + break; + case 'A': + showall = true; + break; + case 'a': + add_dtype(strtodsdigest(isc_commandline_argument)); + break; + case 'C': + cds = true; + break; + case 'c': + classname = isc_commandline_argument; + break; + case 'd': + fprintf(stderr, + "%s: the -d option is deprecated; " + "use -K\n", + program); + /* fall through */ + case 'K': + dir = isc_commandline_argument; + if (strlen(dir) == 0U) { + fatal("directory must be non-empty string"); + } + break; + case 'f': + filename = isc_commandline_argument; + break; + case 'l': + fatal("-l option (DLV lookaside) is obsolete"); + break; + case 's': + usekeyset = true; + break; + case 'T': + emitttl = true; + ttl = strtottl(isc_commandline_argument); + break; + case 'v': + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case 'F': + /* Reserved for FIPS mode */ + FALLTHROUGH; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + rdclass = strtoclass(classname); + + if (usekeyset && filename != NULL) { + fatal("cannot use both -s and -f"); + } + + /* When not using -f, -A is implicit */ + if (filename == NULL) { + showall = true; + } + + /* Default digest type if none specified. */ + if (dtype[0] == 0) { + dtype[0] = DNS_DSDIGEST_SHA256; + } + + /* + * Use local variable arg1 so that clang can correctly analyse + * reachable paths rather than 'argc < isc_commandline_index + 1'. + */ + arg1 = argv[isc_commandline_index]; + if (arg1 == NULL && filename == NULL) { + fatal("the key file name was not specified"); + } + if (arg1 != NULL && argv[isc_commandline_index + 1] != NULL) { + fatal("extraneous arguments"); + } + + result = dst_lib_init(mctx, NULL); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", + isc_result_totext(result)); + } + + setup_logging(mctx, &log); + + dns_rdataset_init(&rdataset); + + if (usekeyset || filename != NULL) { + if (arg1 == NULL) { + /* using file name as the zone name */ + namestr = filename; + } else { + namestr = arg1; + } + + result = initname(namestr); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize name %s", namestr); + } + + if (usekeyset) { + result = loadkeyset(dir, &rdataset); + } else { + INSIST(filename != NULL); + result = loadset(filename, &rdataset); + } + + if (result != ISC_R_SUCCESS) { + fatal("could not load DNSKEY set: %s\n", + isc_result_totext(result)); + } + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdata_init(&rdata); + dns_rdataset_current(&rdataset, &rdata); + + if (verbose > 2) { + logkey(&rdata); + } + + emits(showall, cds, &rdata); + } + } else { + unsigned char key_buf[DST_KEY_MAXSIZE]; + + loadkey(arg1, key_buf, DST_KEY_MAXSIZE, &rdata); + + emits(showall, cds, &rdata); + } + + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + cleanup_logging(&log); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + + fflush(stdout); + if (ferror(stdout)) { + fprintf(stderr, "write error\n"); + return (1); + } else { + return (0); + } +} diff --git a/bin/dnssec/dnssec-dsfromkey.rst b/bin/dnssec/dnssec-dsfromkey.rst new file mode 100644 index 0000000..9ca025a --- /dev/null +++ b/bin/dnssec/dnssec-dsfromkey.rst @@ -0,0 +1,159 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-dsfromkey +.. program:: dnssec-dsfromkey +.. _man_dnssec-dsfromkey: + +dnssec-dsfromkey - DNSSEC DS RR generation tool +----------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-K** directory] {keyfile} + +:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-c** class] [**-A**] {**-f** file} [dnsname] + +:program:`dnssec-dsfromkey` [ **-1** | **-2** | **-a** alg ] [ **-C** ] [**-T** TTL] [**-v** level] [**-c** class] [**-K** directory] {**-s**} {dnsname} + +:program:`dnssec-dsfromkey` [ **-h** | **-V** ] + +Description +~~~~~~~~~~~ + +The :program:`dnssec-dsfromkey` command outputs DS (Delegation Signer) resource records +(RRs), or CDS (Child DS) RRs with the :option:`-C` option. + +By default, only KSKs are converted (keys with flags = 257). The +:option:`-A` option includes ZSKs (flags = 256). Revoked keys are never +included. + +The input keys can be specified in a number of ways: + +By default, :program:`dnssec-dsfromkey` reads a key file named in the format +``Knnnn.+aaa+iiiii.key``, as generated by :iscman:`dnssec-keygen`. + +With the :option:`-f file <-f>` option, :program:`dnssec-dsfromkey` reads keys from a zone +file or partial zone file (which can contain just the DNSKEY records). + +With the :option:`-s` option, :program:`dnssec-dsfromkey` reads a ``keyset-`` file, +as generated by :iscman:`dnssec-keygen` :option:`-C`. + +Options +~~~~~~~ + +.. option:: -1 + + This option is an abbreviation for :option:`-a SHA1 <-a>`. + +.. option:: -2 + + This option is an abbreviation for :option:`-a SHA-256 <-a>`. + +.. option:: -a algorithm + + This option specifies a digest algorithm to use when converting DNSKEY records to + DS records. This option can be repeated, so that multiple DS records + are created for each DNSKEY record. + + The algorithm must be one of SHA-1, SHA-256, or SHA-384. These values + are case-insensitive, and the hyphen may be omitted. If no algorithm + is specified, the default is SHA-256. + +.. option:: -A + + This option indicates that ZSKs are to be included when generating DS records. Without this option, only + keys which have the KSK flag set are converted to DS records and + printed. This option is only useful in :option:`-f` zone file mode. + +.. option:: -c class + + This option specifies the DNS class; the default is IN. This option is only useful in :option:`-s` keyset + or :option:`-f` zone file mode. + +.. option:: -C + + This option generates CDS records rather than DS records. + +.. option:: -f file + + This option sets zone file mode, in which the final dnsname argument of :program:`dnssec-dsfromkey` is the + DNS domain name of a zone whose master file can be read from + ``file``. If the zone name is the same as ``file``, then it may be + omitted. + + If ``file`` is ``-``, then the zone data is read from the standard + input. This makes it possible to use the output of the :iscman:`dig` + command as input, as in: + + ``dig dnskey example.com | dnssec-dsfromkey -f - example.com`` + +.. option:: -h + + This option prints usage information. + +.. option:: -K directory + + This option tells BIND 9 to look for key files or ``keyset-`` files in ``directory``. + +.. option:: -s + + This option enables keyset mode, in which the final dnsname argument from :program:`dnssec-dsfromkey` is the DNS + domain name used to locate a ``keyset-`` file. + +.. option:: -T TTL + + This option specifies the TTL of the DS records. By default the TTL is omitted. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -V + + This option prints version information. + +Example +~~~~~~~ + +To build the SHA-256 DS RR from the ``Kexample.com.+003+26160`` keyfile, +issue the following command: + +``dnssec-dsfromkey -2 Kexample.com.+003+26160`` + +The command returns something similar to: + +``example.com. IN DS 26160 5 2 3A1EADA7A74B8D0BA86726B0C227AA85AB8BBD2B2004F41A868A54F0C5EA0B94`` + +Files +~~~~~ + +The keyfile can be designated by the key identification +``Knnnn.+aaa+iiiii`` or the full file name ``Knnnn.+aaa+iiiii.key``, as +generated by :iscman:`dnssec-keygen`. + +The keyset file name is built from the ``directory``, the string +``keyset-``, and the ``dnsname``. + +Caveat +~~~~~~ + +A keyfile error may return "file not found," even if the file exists. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, :iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, +:rfc:`3658` (DS RRs), :rfc:`4509` (SHA-256 for DS RRs), +:rfc:`6605` (SHA-384 for DS RRs), :rfc:`7344` (CDS and CDNSKEY RRs). diff --git a/bin/dnssec/dnssec-importkey.c b/bin/dnssec/dnssec-importkey.c new file mode 100644 index 0000000..441f7c3 --- /dev/null +++ b/bin/dnssec/dnssec-importkey.c @@ -0,0 +1,477 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-importkey"; + +static dns_rdataclass_t rdclass; +static dns_fixedname_t fixed; +static dns_name_t *name = NULL; +static isc_mem_t *mctx = NULL; +static bool setpub = false, setdel = false; +static bool setttl = false; +static isc_stdtime_t pub = 0, del = 0; +static dns_ttl_t ttl = 0; +static isc_stdtime_t syncadd = 0, syncdel = 0; +static bool setsyncadd = false; +static bool setsyncdel = false; + +static isc_result_t +initname(char *setname) { + isc_result_t result; + isc_buffer_t buf; + + name = dns_fixedname_initname(&fixed); + + isc_buffer_init(&buf, setname, strlen(setname)); + isc_buffer_add(&buf, strlen(setname)); + result = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + return (result); +} + +static void +db_load_from_stream(dns_db_t *db, FILE *fp) { + isc_result_t result; + dns_rdatacallbacks_t callbacks; + + dns_rdatacallbacks_init(&callbacks); + result = dns_db_beginload(db, &callbacks); + if (result != ISC_R_SUCCESS) { + fatal("dns_db_beginload failed: %s", isc_result_totext(result)); + } + + result = dns_master_loadstream(fp, name, name, rdclass, 0, &callbacks, + mctx); + if (result != ISC_R_SUCCESS) { + fatal("can't load from input: %s", isc_result_totext(result)); + } + + result = dns_db_endload(db, &callbacks); + if (result != ISC_R_SUCCESS) { + fatal("dns_db_endload failed: %s", isc_result_totext(result)); + } +} + +static isc_result_t +loadset(const char *filename, dns_rdataset_t *rdataset) { + isc_result_t result; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + char setname[DNS_NAME_FORMATSIZE]; + + dns_name_format(name, setname, sizeof(setname)); + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, &db); + if (result != ISC_R_SUCCESS) { + fatal("can't create database"); + } + + if (strcmp(filename, "-") == 0) { + db_load_from_stream(db, stdin); + filename = "input"; + } else { + result = dns_db_load(db, filename, dns_masterformat_text, + DNS_MASTER_NOTTL); + if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { + fatal("can't load %s: %s", filename, + isc_result_totext(result)); + } + } + + result = dns_db_findnode(db, name, false, &node); + if (result != ISC_R_SUCCESS) { + fatal("can't find %s node in %s", setname, filename); + } + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0, + rdataset, NULL); + + if (result == ISC_R_NOTFOUND) { + fatal("no DNSKEY RR for %s in %s", setname, filename); + } else if (result != ISC_R_SUCCESS) { + fatal("dns_db_findrdataset"); + } + + if (node != NULL) { + dns_db_detachnode(db, &node); + } + if (db != NULL) { + dns_db_detach(&db); + } + return (result); +} + +static void +loadkey(char *filename, unsigned char *key_buf, unsigned int key_buf_size, + dns_rdata_t *rdata) { + isc_result_t result; + dst_key_t *key = NULL; + isc_buffer_t keyb; + isc_region_t r; + + dns_rdata_init(rdata); + + isc_buffer_init(&keyb, key_buf, key_buf_size); + + result = dst_key_fromnamedfile(filename, NULL, DST_TYPE_PUBLIC, mctx, + &key); + if (result != ISC_R_SUCCESS) { + fatal("invalid keyfile name %s: %s", filename, + isc_result_totext(result)); + } + + if (verbose > 2) { + char keystr[DST_KEY_FORMATSIZE]; + + dst_key_format(key, keystr, sizeof(keystr)); + fprintf(stderr, "%s: %s\n", program, keystr); + } + + result = dst_key_todns(key, &keyb); + if (result != ISC_R_SUCCESS) { + fatal("can't decode key"); + } + + isc_buffer_usedregion(&keyb, &r); + dns_rdata_fromregion(rdata, dst_key_class(key), dns_rdatatype_dnskey, + &r); + + rdclass = dst_key_class(key); + + name = dns_fixedname_initname(&fixed); + dns_name_copy(dst_key_name(key), name); + + dst_key_free(&key); +} + +static void +emit(const char *dir, dns_rdata_t *rdata) { + isc_result_t result; + char keystr[DST_KEY_FORMATSIZE]; + char pubname[1024]; + char priname[1024]; + isc_buffer_t buf; + dst_key_t *key = NULL, *tmp = NULL; + + isc_buffer_init(&buf, rdata->data, rdata->length); + isc_buffer_add(&buf, rdata->length); + result = dst_key_fromdns(name, rdclass, &buf, mctx, &key); + if (result != ISC_R_SUCCESS) { + fatal("dst_key_fromdns: %s", isc_result_totext(result)); + } + + isc_buffer_init(&buf, pubname, sizeof(pubname)); + result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build public key filename: %s", + isc_result_totext(result)); + } + isc_buffer_init(&buf, priname, sizeof(priname)); + result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build private key filename: %s", + isc_result_totext(result)); + } + + result = dst_key_fromfile( + dst_key_name(key), dst_key_id(key), dst_key_alg(key), + DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir, mctx, &tmp); + if (result == ISC_R_SUCCESS) { + if (dst_key_isprivate(tmp) && !dst_key_isexternal(tmp)) { + fatal("Private key already exists in %s", priname); + } + dst_key_free(&tmp); + } + + dst_key_setexternal(key, true); + if (setpub) { + dst_key_settime(key, DST_TIME_PUBLISH, pub); + } + if (setdel) { + dst_key_settime(key, DST_TIME_DELETE, del); + } + if (setsyncadd) { + dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd); + } + if (setsyncdel) { + dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel); + } + + if (setttl) { + dst_key_setttl(key, ttl); + } + + result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, dir); + if (result != ISC_R_SUCCESS) { + dst_key_format(key, keystr, sizeof(keystr)); + fatal("Failed to write key %s: %s", keystr, + isc_result_totext(result)); + } + printf("%s\n", pubname); + + isc_buffer_clear(&buf); + result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build private key filename: %s", + isc_result_totext(result)); + } + printf("%s\n", priname); + dst_key_free(&key); +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s options [-K dir] keyfile\n\n", program); + fprintf(stderr, " %s options -f file [keyname]\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -f file: read key from zone file\n"); + fprintf(stderr, " -K : directory in which to store " + "the key files\n"); + fprintf(stderr, " -L ttl: set default key TTL\n"); + fprintf(stderr, " -v \n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, " -h: print usage and exit\n"); + fprintf(stderr, "Timing options:\n"); + fprintf(stderr, " -P date/[+-]offset/none: set/unset key " + "publication date\n"); + fprintf(stderr, " -P sync date/[+-]offset/none: set/unset " + "CDS and CDNSKEY publication date\n"); + fprintf(stderr, " -D date/[+-]offset/none: set/unset key " + "deletion date\n"); + fprintf(stderr, " -D sync date/[+-]offset/none: set/unset " + "CDS and CDNSKEY deletion date\n"); + + exit(-1); +} + +int +main(int argc, char **argv) { + char *classname = NULL; + char *filename = NULL, *dir = NULL, *namestr; + char *endp; + int ch; + isc_result_t result; + isc_log_t *log = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata; + isc_stdtime_t now; + + dns_rdata_init(&rdata); + isc_stdtime_get(&now); + + if (argc == 1) { + usage(); + } + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + +#define CMDLINE_FLAGS "D:f:hK:L:P:v:V" + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'D': + /* -Dsync ? */ + if (isoptarg("sync", argv, usage)) { + if (setsyncdel) { + fatal("-D sync specified more than " + "once"); + } + + syncdel = strtotime(isc_commandline_argument, + now, now, &setsyncdel); + break; + } + /* -Ddnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setdel) { + fatal("-D specified more than once"); + } + + del = strtotime(isc_commandline_argument, now, now, + &setdel); + break; + case 'K': + dir = isc_commandline_argument; + if (strlen(dir) == 0U) { + fatal("directory must be non-empty string"); + } + break; + case 'L': + ttl = strtottl(isc_commandline_argument); + setttl = true; + break; + case 'P': + /* -Psync ? */ + if (isoptarg("sync", argv, usage)) { + if (setsyncadd) { + fatal("-P sync specified more than " + "once"); + } + + syncadd = strtotime(isc_commandline_argument, + now, now, &setsyncadd); + break; + } + /* -Pdnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setpub) { + fatal("-P specified more than once"); + } + + pub = strtotime(isc_commandline_argument, now, now, + &setpub); + break; + case 'f': + filename = isc_commandline_argument; + break; + case 'v': + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + rdclass = strtoclass(classname); + + if (argc < isc_commandline_index + 1 && filename == NULL) { + fatal("the key file name was not specified"); + } + if (argc > isc_commandline_index + 1) { + fatal("extraneous arguments"); + } + + result = dst_lib_init(mctx, NULL); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", + isc_result_totext(result)); + } + + setup_logging(mctx, &log); + + dns_rdataset_init(&rdataset); + + if (filename != NULL) { + if (argc < isc_commandline_index + 1) { + /* using filename as zone name */ + namestr = filename; + } else { + namestr = argv[isc_commandline_index]; + } + + result = initname(namestr); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize name %s", namestr); + } + + result = loadset(filename, &rdataset); + + if (result != ISC_R_SUCCESS) { + fatal("could not load DNSKEY set: %s\n", + isc_result_totext(result)); + } + + for (result = dns_rdataset_first(&rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdata_init(&rdata); + dns_rdataset_current(&rdataset, &rdata); + emit(dir, &rdata); + } + } else { + unsigned char key_buf[DST_KEY_MAXSIZE]; + + loadkey(argv[isc_commandline_index], key_buf, DST_KEY_MAXSIZE, + &rdata); + + emit(dir, &rdata); + } + + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + cleanup_logging(&log); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + + fflush(stdout); + if (ferror(stdout)) { + fprintf(stderr, "write error\n"); + return (1); + } else { + return (0); + } +} diff --git a/bin/dnssec/dnssec-importkey.rst b/bin/dnssec/dnssec-importkey.rst new file mode 100644 index 0000000..8f6a6b3 --- /dev/null +++ b/bin/dnssec/dnssec-importkey.rst @@ -0,0 +1,142 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-importkey +.. program:: dnssec-importkey +.. _man_dnssec-importkey: + +dnssec-importkey - import DNSKEY records from external systems so they can be managed +------------------------------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-importkey` [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** sync date/offset] [**-D** date/offset] [**-D** sync date/offset] [**-h**] [**-v** level] [**-V**] {keyfile} + +:program:`dnssec-importkey` {**-f** filename} [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** sync date/offset] [**-D** date/offset] [**-D** sync date/offset] [**-h**] [**-v** level] [**-V**] [dnsname] + +Description +~~~~~~~~~~~ + +:program:`dnssec-importkey` reads a public DNSKEY record and generates a pair +of .key/.private files. The DNSKEY record may be read from an +existing .key file, in which case a corresponding .private file is +generated, or it may be read from any other file or from the standard +input, in which case both .key and .private files are generated. + +The newly created .private file does *not* contain private key data, and +cannot be used for signing. However, having a .private file makes it +possible to set publication (:option:`-P`) and deletion (:option:`-D`) times for the +key, which means the public key can be added to and removed from the +DNSKEY RRset on schedule even if the true private key is stored offline. + +Options +~~~~~~~ + +.. option:: -f filename + + This option indicates the zone file mode. Instead of a public keyfile name, the argument is the + DNS domain name of a zone master file, which can be read from + ``filename``. If the domain name is the same as ``filename``, then it may be + omitted. + + If ``filename`` is set to ``"-"``, then the zone data is read from the + standard input. + +.. option:: -K directory + + This option sets the directory in which the key files are to reside. + +.. option:: -L ttl + + This option sets the default TTL to use for this key when it is converted into a + DNSKEY RR. This is the TTL used when the key is imported into a zone, + unless there was already a DNSKEY RRset in + place, in which case the existing TTL takes precedence. Setting the default TTL to ``0`` or ``none`` + removes it from the key. + +.. option:: -h + + This option emits a usage message and exits. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -V + + This option prints version information. + +Timing Options +~~~~~~~~~~~~~~ + +Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS. +(which is the format used inside key files), +or 'Day Mon DD HH:MM:SS YYYY' (as printed by ``dnssec-settime -p``), +or UNIX epoch time (as printed by ``dnssec-settime -up``), +or the literal ``now``. + +The argument can be followed by ``+`` or ``-`` and an offset from the +given time. The literal ``now`` can be omitted before an offset. The +offset can be followed by one of the suffixes ``y``, ``mo``, ``w``, +``d``, ``h``, or ``mi``, so that it is computed in years (defined as +365 24-hour days, ignoring leap years), months (defined as 30 24-hour +days), weeks, days, hours, or minutes, respectively. Without a suffix, +the offset is computed in seconds. + +To explicitly prevent a date from being set, use ``none``, ``never``, +or ``unset``. + +All these formats are case-insensitive. + +.. option:: -P date/offset + + This option sets the date on which a key is to be published to the zone. After + that date, the key is included in the zone but is not used + to sign it. + + .. program:: dnssec-importkey -P + .. option:: sync date/offset + + This option sets the date on which CDS and CDNSKEY records that match this key + are to be published to the zone. + +.. program:: dnssec-importkey + +.. option:: -D date/offset + + This option sets the date on which the key is to be deleted. After that date, the + key is no longer included in the zone. (However, it may remain in the key + repository.) + + .. program:: dnssec-importkey -D + .. option:: sync date/offset + + This option sets the date on which the CDS and CDNSKEY records that match this + key are to be deleted. + +.. program:: dnssec-importkey + + +Files +~~~~~ + +A keyfile can be designed by the key identification ``Knnnn.+aaa+iiiii`` +or the full file name ``Knnnn.+aaa+iiiii.key``, as generated by +:iscman:`dnssec-keygen`. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, :iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, +:rfc:`5011`. diff --git a/bin/dnssec/dnssec-keyfromlabel.c b/bin/dnssec/dnssec-keyfromlabel.c new file mode 100644 index 0000000..53ca718 --- /dev/null +++ b/bin/dnssec/dnssec-keyfromlabel.c @@ -0,0 +1,760 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +#define MAX_RSA 4096 /* should be long enough... */ + +const char *program = "dnssec-keyfromlabel"; + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s -l label [options] name\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "Required options:\n"); + fprintf(stderr, " -l label: label of the key pair\n"); + fprintf(stderr, " name: owner of the key\n"); + fprintf(stderr, "Other options:\n"); + fprintf(stderr, " -a algorithm: \n" + " DH | RSASHA1 |\n" + " NSEC3RSASHA1 |\n" + " RSASHA256 | RSASHA512 |\n" + " ECDSAP256SHA256 | ECDSAP384SHA384 |\n" + " ED25519 | ED448\n"); + fprintf(stderr, " -3: use NSEC3-capable algorithm\n"); + fprintf(stderr, " -c class (default: IN)\n"); + fprintf(stderr, " -E :\n"); + fprintf(stderr, " name of an OpenSSL engine to use\n"); + fprintf(stderr, " -f keyflag: KSK | REVOKE\n"); + fprintf(stderr, " -K directory: directory in which to place " + "key files\n"); + fprintf(stderr, " -k: generate a TYPE=KEY key\n"); + fprintf(stderr, " -L ttl: default key TTL\n"); + fprintf(stderr, " -n nametype: ZONE | HOST | ENTITY | USER | " + "OTHER\n"); + fprintf(stderr, " (DNSKEY generation defaults to ZONE\n"); + fprintf(stderr, " -p protocol: default: 3 [dnssec]\n"); + fprintf(stderr, " -t type: " + "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF " + "(default: AUTHCONF)\n"); + fprintf(stderr, " -y: permit keys that might collide\n"); + fprintf(stderr, " -v verbose level\n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, "Date options:\n"); + fprintf(stderr, " -P date/[+-]offset: set key publication date\n"); + fprintf(stderr, " -P sync date/[+-]offset: set CDS and CDNSKEY " + "publication date\n"); + fprintf(stderr, " -A date/[+-]offset: set key activation date\n"); + fprintf(stderr, " -R date/[+-]offset: set key revocation date\n"); + fprintf(stderr, " -I date/[+-]offset: set key inactivation date\n"); + fprintf(stderr, " -D date/[+-]offset: set key deletion date\n"); + fprintf(stderr, " -D sync date/[+-]offset: set CDS and CDNSKEY " + "deletion date\n"); + fprintf(stderr, " -G: generate key only; do not set -P or -A\n"); + fprintf(stderr, " -C: generate a backward-compatible key, omitting" + " all dates\n"); + fprintf(stderr, " -S : generate a successor to an existing " + "key\n"); + fprintf(stderr, " -i : prepublication interval for " + "successor key " + "(default: 30 days)\n"); + fprintf(stderr, "Output:\n"); + fprintf(stderr, " K++.key, " + "K++.private\n"); + + exit(-1); +} + +int +main(int argc, char **argv) { + char *algname = NULL, *freeit = NULL; + char *nametype = NULL, *type = NULL; + const char *directory = NULL; + const char *predecessor = NULL; + dst_key_t *prevkey = NULL; + const char *engine = NULL; + char *classname = NULL; + char *endp; + dst_key_t *key = NULL; + dns_fixedname_t fname; + dns_name_t *name; + uint16_t flags = 0, kskflag = 0, revflag = 0; + dns_secalg_t alg; + bool oldstyle = false; + isc_mem_t *mctx = NULL; + int ch; + int protocol = -1, signatory = 0; + isc_result_t ret; + isc_textregion_t r; + char filename[255]; + isc_buffer_t buf; + isc_log_t *log = NULL; + dns_rdataclass_t rdclass; + int options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC; + char *label = NULL; + dns_ttl_t ttl = 0; + isc_stdtime_t publish = 0, activate = 0, revoke = 0; + isc_stdtime_t inactive = 0, deltime = 0; + isc_stdtime_t now; + int prepub = -1; + bool setpub = false, setact = false; + bool setrev = false, setinact = false; + bool setdel = false, setttl = false; + bool unsetpub = false, unsetact = false; + bool unsetrev = false, unsetinact = false; + bool unsetdel = false; + bool genonly = false; + bool use_nsec3 = false; + bool avoid_collisions = true; + bool exact; + unsigned char c; + isc_stdtime_t syncadd = 0, syncdel = 0; + bool unsetsyncadd = false, setsyncadd = false; + bool unsetsyncdel = false, setsyncdel = false; + + if (argc == 1) { + usage(); + } + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + + isc_stdtime_get(&now); + +#define CMDLINE_FLAGS "3A:a:Cc:D:E:Ff:GhI:i:kK:L:l:n:P:p:R:S:t:v:Vy" + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case '3': + use_nsec3 = true; + break; + case 'a': + algname = isc_commandline_argument; + break; + case 'C': + oldstyle = true; + break; + case 'c': + classname = isc_commandline_argument; + break; + case 'E': + engine = isc_commandline_argument; + break; + case 'f': + c = (unsigned char)(isc_commandline_argument[0]); + if (toupper(c) == 'K') { + kskflag = DNS_KEYFLAG_KSK; + } else if (toupper(c) == 'R') { + revflag = DNS_KEYFLAG_REVOKE; + } else { + fatal("unknown flag '%s'", + isc_commandline_argument); + } + break; + case 'K': + directory = isc_commandline_argument; + ret = try_dir(directory); + if (ret != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", directory, + isc_result_totext(ret)); + } + break; + case 'k': + options |= DST_TYPE_KEY; + break; + case 'L': + ttl = strtottl(isc_commandline_argument); + setttl = true; + break; + case 'l': + label = isc_mem_strdup(mctx, isc_commandline_argument); + break; + case 'n': + nametype = isc_commandline_argument; + break; + case 'p': + protocol = strtol(isc_commandline_argument, &endp, 10); + if (*endp != '\0' || protocol < 0 || protocol > 255) { + fatal("-p must be followed by a number " + "[0..255]"); + } + break; + case 't': + type = isc_commandline_argument; + break; + case 'v': + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case 'y': + avoid_collisions = false; + break; + case 'G': + genonly = true; + break; + case 'P': + /* -Psync ? */ + if (isoptarg("sync", argv, usage)) { + if (unsetsyncadd || setsyncadd) { + fatal("-P sync specified more than " + "once"); + } + + syncadd = strtotime(isc_commandline_argument, + now, now, &setsyncadd); + unsetsyncadd = !setsyncadd; + break; + } + /* -Pdnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setpub || unsetpub) { + fatal("-P specified more than once"); + } + + publish = strtotime(isc_commandline_argument, now, now, + &setpub); + unsetpub = !setpub; + break; + case 'A': + if (setact || unsetact) { + fatal("-A specified more than once"); + } + + activate = strtotime(isc_commandline_argument, now, now, + &setact); + unsetact = !setact; + break; + case 'R': + if (setrev || unsetrev) { + fatal("-R specified more than once"); + } + + revoke = strtotime(isc_commandline_argument, now, now, + &setrev); + unsetrev = !setrev; + break; + case 'I': + if (setinact || unsetinact) { + fatal("-I specified more than once"); + } + + inactive = strtotime(isc_commandline_argument, now, now, + &setinact); + unsetinact = !setinact; + break; + case 'D': + /* -Dsync ? */ + if (isoptarg("sync", argv, usage)) { + if (unsetsyncdel || setsyncdel) { + fatal("-D sync specified more than " + "once"); + } + + syncdel = strtotime(isc_commandline_argument, + now, now, &setsyncdel); + unsetsyncdel = !setsyncdel; + break; + } + /* -Ddnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setdel || unsetdel) { + fatal("-D specified more than once"); + } + + deltime = strtotime(isc_commandline_argument, now, now, + &setdel); + unsetdel = !setdel; + break; + case 'S': + predecessor = isc_commandline_argument; + break; + case 'i': + prepub = strtottl(isc_commandline_argument); + break; + case 'F': + /* Reserved for FIPS mode */ + FALLTHROUGH; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + ret = dst_lib_init(mctx, engine); + if (ret != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", isc_result_totext(ret)); + } + + setup_logging(mctx, &log); + + if (predecessor == NULL) { + if (label == NULL) { + fatal("the key label was not specified"); + } + if (argc < isc_commandline_index + 1) { + fatal("the key name was not specified"); + } + if (argc > isc_commandline_index + 1) { + fatal("extraneous arguments"); + } + + name = dns_fixedname_initname(&fname); + isc_buffer_init(&buf, argv[isc_commandline_index], + strlen(argv[isc_commandline_index])); + isc_buffer_add(&buf, strlen(argv[isc_commandline_index])); + ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + if (ret != ISC_R_SUCCESS) { + fatal("invalid key name %s: %s", + argv[isc_commandline_index], + isc_result_totext(ret)); + } + + if (strchr(label, ':') == NULL) { + char *l; + int len; + + len = strlen(label) + 8; + l = isc_mem_allocate(mctx, len); + snprintf(l, len, "pkcs11:%s", label); + isc_mem_free(mctx, label); + label = l; + } + + if (algname == NULL) { + fatal("no algorithm specified"); + } + + r.base = algname; + r.length = strlen(algname); + ret = dns_secalg_fromtext(&alg, &r); + if (ret != ISC_R_SUCCESS) { + fatal("unknown algorithm %s", algname); + } + if (alg == DST_ALG_DH) { + options |= DST_TYPE_KEY; + } + + if (use_nsec3) { + switch (alg) { + case DST_ALG_RSASHA1: + alg = DST_ALG_NSEC3RSASHA1; + break; + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + case DST_ALG_ECDSA256: + case DST_ALG_ECDSA384: + case DST_ALG_ED25519: + case DST_ALG_ED448: + break; + default: + fatal("%s is incompatible with NSEC3; " + "do not use the -3 option", + algname); + } + } + + if (type != NULL && (options & DST_TYPE_KEY) != 0) { + if (strcasecmp(type, "NOAUTH") == 0) { + flags |= DNS_KEYTYPE_NOAUTH; + } else if (strcasecmp(type, "NOCONF") == 0) { + flags |= DNS_KEYTYPE_NOCONF; + } else if (strcasecmp(type, "NOAUTHCONF") == 0) { + flags |= (DNS_KEYTYPE_NOAUTH | + DNS_KEYTYPE_NOCONF); + } else if (strcasecmp(type, "AUTHCONF") == 0) { + /* nothing */ + } else { + fatal("invalid type %s", type); + } + } + + if (!oldstyle && prepub > 0) { + if (setpub && setact && (activate - prepub) < publish) { + fatal("Activation and publication dates " + "are closer together than the\n\t" + "prepublication interval."); + } + + if (!setpub && !setact) { + setpub = setact = true; + publish = now; + activate = now + prepub; + } else if (setpub && !setact) { + setact = true; + activate = publish + prepub; + } else if (setact && !setpub) { + setpub = true; + publish = activate - prepub; + } + + if ((activate - prepub) < now) { + fatal("Time until activation is shorter " + "than the\n\tprepublication interval."); + } + } + } else { + char keystr[DST_KEY_FORMATSIZE]; + isc_stdtime_t when; + int major, minor; + + if (prepub == -1) { + prepub = (30 * 86400); + } + + if (algname != NULL) { + fatal("-S and -a cannot be used together"); + } + if (nametype != NULL) { + fatal("-S and -n cannot be used together"); + } + if (type != NULL) { + fatal("-S and -t cannot be used together"); + } + if (setpub || unsetpub) { + fatal("-S and -P cannot be used together"); + } + if (setact || unsetact) { + fatal("-S and -A cannot be used together"); + } + if (use_nsec3) { + fatal("-S and -3 cannot be used together"); + } + if (oldstyle) { + fatal("-S and -C cannot be used together"); + } + if (genonly) { + fatal("-S and -G cannot be used together"); + } + + ret = dst_key_fromnamedfile(predecessor, directory, + DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, + mctx, &prevkey); + if (ret != ISC_R_SUCCESS) { + fatal("Invalid keyfile %s: %s", predecessor, + isc_result_totext(ret)); + } + if (!dst_key_isprivate(prevkey)) { + fatal("%s is not a private key", predecessor); + } + + name = dst_key_name(prevkey); + alg = dst_key_alg(prevkey); + flags = dst_key_flags(prevkey); + + dst_key_format(prevkey, keystr, sizeof(keystr)); + dst_key_getprivateformat(prevkey, &major, &minor); + if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { + fatal("Key %s has incompatible format version %d.%d\n\t" + "It is not possible to generate a successor key.", + keystr, major, minor); + } + + ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when); + if (ret != ISC_R_SUCCESS) { + fatal("Key %s has no activation date.\n\t" + "You must use dnssec-settime -A to set one " + "before generating a successor.", + keystr); + } + + ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE, &activate); + if (ret != ISC_R_SUCCESS) { + fatal("Key %s has no inactivation date.\n\t" + "You must use dnssec-settime -I to set one " + "before generating a successor.", + keystr); + } + + publish = activate - prepub; + if (publish < now) { + fatal("Key %s becomes inactive\n\t" + "sooner than the prepublication period " + "for the new key ends.\n\t" + "Either change the inactivation date with " + "dnssec-settime -I,\n\t" + "or use the -i option to set a shorter " + "prepublication interval.", + keystr); + } + + ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when); + if (ret != ISC_R_SUCCESS) { + fprintf(stderr, + "%s: WARNING: Key %s has no removal " + "date;\n\t it will remain in the zone " + "indefinitely after rollover.\n\t " + "You can use dnssec-settime -D to " + "change this.\n", + program, keystr); + } + + setpub = setact = true; + } + + if (nametype == NULL) { + if ((options & DST_TYPE_KEY) != 0) { /* KEY */ + fatal("no nametype specified"); + } + flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */ + } else if (strcasecmp(nametype, "zone") == 0) { + flags |= DNS_KEYOWNER_ZONE; + } else if ((options & DST_TYPE_KEY) != 0) { /* KEY */ + if (strcasecmp(nametype, "host") == 0 || + strcasecmp(nametype, "entity") == 0) + { + flags |= DNS_KEYOWNER_ENTITY; + } else if (strcasecmp(nametype, "user") == 0) { + flags |= DNS_KEYOWNER_USER; + } else { + fatal("invalid KEY nametype %s", nametype); + } + } else if (strcasecmp(nametype, "other") != 0) { /* DNSKEY */ + fatal("invalid DNSKEY nametype %s", nametype); + } + + rdclass = strtoclass(classname); + + if (directory == NULL) { + directory = "."; + } + + if ((options & DST_TYPE_KEY) != 0) { /* KEY */ + flags |= signatory; + } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */ + flags |= kskflag; + flags |= revflag; + } + + if (protocol == -1) { + protocol = DNS_KEYPROTO_DNSSEC; + } else if ((options & DST_TYPE_KEY) == 0 && + protocol != DNS_KEYPROTO_DNSSEC) + { + fatal("invalid DNSKEY protocol: %d", protocol); + } + + if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { + if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) { + fatal("specified null key with signing authority"); + } + } + + if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE && + alg == DNS_KEYALG_DH) + { + fatal("a key with algorithm '%s' cannot be a zone key", + algname); + } + + isc_buffer_init(&buf, filename, sizeof(filename) - 1); + + /* associate the key */ + ret = dst_key_fromlabel(name, alg, flags, protocol, rdclass, engine, + label, NULL, mctx, &key); + + if (ret != ISC_R_SUCCESS) { + char namestr[DNS_NAME_FORMATSIZE]; + char algstr[DNS_SECALG_FORMATSIZE]; + dns_name_format(name, namestr, sizeof(namestr)); + dns_secalg_format(alg, algstr, sizeof(algstr)); + fatal("failed to get key %s/%s: %s", namestr, algstr, + isc_result_totext(ret)); + UNREACHABLE(); + exit(-1); + } + + /* + * Set key timing metadata (unless using -C) + * + * Publish and activation dates are set to "now" by default, but + * can be overridden. Creation date is always set to "now". + */ + if (!oldstyle) { + dst_key_settime(key, DST_TIME_CREATED, now); + + if (genonly && (setpub || setact)) { + fatal("cannot use -G together with -P or -A options"); + } + + if (setpub) { + dst_key_settime(key, DST_TIME_PUBLISH, publish); + } else if (setact) { + dst_key_settime(key, DST_TIME_PUBLISH, activate); + } else if (!genonly && !unsetpub) { + dst_key_settime(key, DST_TIME_PUBLISH, now); + } + + if (setact) { + dst_key_settime(key, DST_TIME_ACTIVATE, activate); + } else if (!genonly && !unsetact) { + dst_key_settime(key, DST_TIME_ACTIVATE, now); + } + + if (setrev) { + if (kskflag == 0) { + fprintf(stderr, + "%s: warning: Key is " + "not flagged as a KSK, but -R " + "was used. Revoking a ZSK is " + "legal, but undefined.\n", + program); + } + dst_key_settime(key, DST_TIME_REVOKE, revoke); + } + + if (setinact) { + dst_key_settime(key, DST_TIME_INACTIVE, inactive); + } + + if (setdel) { + dst_key_settime(key, DST_TIME_DELETE, deltime); + } + if (setsyncadd) { + dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd); + } + if (setsyncdel) { + dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel); + } + } else { + if (setpub || setact || setrev || setinact || setdel || + unsetpub || unsetact || unsetrev || unsetinact || + unsetdel || genonly || setsyncadd || setsyncdel) + { + fatal("cannot use -C together with " + "-P, -A, -R, -I, -D, or -G options"); + } + /* + * Compatibility mode: Private-key-format + * should be set to 1.2. + */ + dst_key_setprivateformat(key, 1, 2); + } + + /* Set default key TTL */ + if (setttl) { + dst_key_setttl(key, ttl); + } + + /* + * Do not overwrite an existing key. Warn LOUDLY if there + * is a risk of ID collision due to this key or another key + * being revoked. + */ + if (key_collision(key, name, directory, mctx, &exact)) { + isc_buffer_clear(&buf); + ret = dst_key_buildfilename(key, 0, directory, &buf); + if (ret != ISC_R_SUCCESS) { + fatal("dst_key_buildfilename returned: %s\n", + isc_result_totext(ret)); + } + if (exact) { + fatal("%s: %s already exists\n", program, filename); + } + + if (avoid_collisions) { + fatal("%s: %s could collide with another key upon " + "revokation\n", + program, filename); + } + + fprintf(stderr, + "%s: WARNING: Key %s could collide with " + "another key upon revokation. If you plan " + "to revoke keys, destroy this key and " + "generate a different one.\n", + program, filename); + } + + ret = dst_key_tofile(key, options, directory); + if (ret != ISC_R_SUCCESS) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + fatal("failed to write key %s: %s\n", keystr, + isc_result_totext(ret)); + } + + isc_buffer_clear(&buf); + ret = dst_key_buildfilename(key, 0, NULL, &buf); + if (ret != ISC_R_SUCCESS) { + fatal("dst_key_buildfilename returned: %s\n", + isc_result_totext(ret)); + } + printf("%s\n", filename); + dst_key_free(&key); + if (prevkey != NULL) { + dst_key_free(&prevkey); + } + + cleanup_logging(&log); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_free(mctx, label); + isc_mem_destroy(&mctx); + + if (freeit != NULL) { + free(freeit); + } + + return (0); +} diff --git a/bin/dnssec/dnssec-keyfromlabel.rst b/bin/dnssec/dnssec-keyfromlabel.rst new file mode 100644 index 0000000..098feb9 --- /dev/null +++ b/bin/dnssec/dnssec-keyfromlabel.rst @@ -0,0 +1,289 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-keyfromlabel +.. program:: dnssec-keyfromlabel +.. _man_dnssec-keyfromlabel: + +dnssec-keyfromlabel - DNSSEC key generation tool +------------------------------------------------ + +Synopsis +~~~~~~~~ + +:program:`dnssec-keyfromlabel` {**-l** label} [**-3**] [**-a** algorithm] [**-A** date/offset] [**-c** class] [**-D** date/offset] [**-D** sync date/offset] [**-E** engine] [**-f** flag] [**-G**] [**-I** date/offset] [**-i** interval] [**-k**] [**-K** directory] [**-L** ttl] [**-n** nametype] [**-P** date/offset] [**-P** sync date/offset] [**-p** protocol] [**-R** date/offset] [**-S** key] [**-t** type] [**-v** level] [**-V**] [**-y**] {name} + +Description +~~~~~~~~~~~ + +:program:`dnssec-keyfromlabel` generates a pair of key files that reference a +key object stored in a cryptographic hardware service module (HSM). The +private key file can be used for DNSSEC signing of zone data as if it +were a conventional signing key created by :iscman:`dnssec-keygen`, but the +key material is stored within the HSM and the actual signing takes +place there. + +The ``name`` of the key is specified on the command line. This must +match the name of the zone for which the key is being generated. + +Options +~~~~~~~ + +.. option:: -a algorithm + + This option selects the cryptographic algorithm. The value of ``algorithm`` must + be one of RSASHA1, NSEC3RSASHA1, RSASHA256, RSASHA512, + ECDSAP256SHA256, ECDSAP384SHA384, ED25519, or ED448. + + These values are case-insensitive. In some cases, abbreviations are + supported, such as ECDSA256 for ECDSAP256SHA256 and ECDSA384 for + ECDSAP384SHA384. If RSASHA1 is specified along with the :option:`-3` + option, then NSEC3RSASHA1 is used instead. + + This option is mandatory except when using the + :option:`-S` option, which copies the algorithm from the predecessory key. + + .. versionchanged:: 9.12.0 + The default value RSASHA1 for newly generated keys was removed. + +.. option:: -3 + + This option uses an NSEC3-capable algorithm to generate a DNSSEC key. If this + option is used with an algorithm that has both NSEC and NSEC3 + versions, then the NSEC3 version is used; for example, + ``dnssec-keygen -3a RSASHA1`` specifies the NSEC3RSASHA1 algorithm. + +.. option:: -E engine + + This option specifies the cryptographic hardware to use. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -l label + + This option specifies the label for a key pair in the crypto hardware. + + When BIND 9 is built with OpenSSL-based PKCS#11 support, the label is + an arbitrary string that identifies a particular key. It may be + preceded by an optional OpenSSL engine name, followed by a colon, as + in ``pkcs11:keylabel``. + +.. option:: -n nametype + + This option specifies the owner type of the key. The value of ``nametype`` must + either be ZONE (for a DNSSEC zone key (KEY/DNSKEY)), HOST or ENTITY + (for a key associated with a host (KEY)), USER (for a key associated + with a user (KEY)), or OTHER (DNSKEY). These values are + case-insensitive. + +.. option:: -C + + This option enables compatibility mode, which generates an old-style key, without any metadata. + By default, :program:`dnssec-keyfromlabel` includes the key's creation + date in the metadata stored with the private key; other dates may + be set there as well, including publication date, activation date, etc. Keys + that include this data may be incompatible with older versions of + BIND; the :option:`-C` option suppresses them. + +.. option:: -c class + + This option indicates that the DNS record containing the key should have the + specified class. If not specified, class IN is used. + +.. option:: -f flag + + This option sets the specified flag in the ``flag`` field of the KEY/DNSKEY record. + The only recognized flags are KSK (Key-Signing Key) and REVOKE. + +.. option:: -G + + This option generates a key, but does not publish it or sign with it. This option is + incompatible with :option:`-P` and :option:`-A`. + +.. option:: -h + + This option prints a short summary of the options and arguments to + :program:`dnssec-keyfromlabel`. + +.. option:: -K directory + + This option sets the directory in which the key files are to be written. + +.. option:: -k + + This option generates KEY records rather than DNSKEY records. + +.. option:: -L ttl + + This option sets the default TTL to use for this key when it is converted into a + DNSKEY RR. This is the TTL used when the key is imported into a zone, + unless there was already a DNSKEY RRset in + place, in which case the existing TTL would take precedence. Setting + the default TTL to ``0`` or ``none`` removes it. + +.. option:: -p protocol + + This option sets the protocol value for the key. The protocol is a number between + 0 and 255. The default is 3 (DNSSEC). Other possible values for this + argument are listed in :rfc:`2535` and its successors. + +.. option:: -S key + + This option generates a key as an explicit successor to an existing key. The name, + algorithm, size, and type of the key are set to match the + predecessor. The activation date of the new key is set to the + inactivation date of the existing one. The publication date is + set to the activation date minus the prepublication interval, which + defaults to 30 days. + +.. option:: -t type + + This option indicates the type of the key. ``type`` must be one of AUTHCONF, + NOAUTHCONF, NOAUTH, or NOCONF. The default is AUTHCONF. AUTH refers + to the ability to authenticate data, and CONF to the ability to encrypt + data. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -V + + This option prints version information. + +.. option:: -y + + This option allows DNSSEC key files to be generated even if the key ID would + collide with that of an existing key, in the event of either key + being revoked. (This is only safe to enable if + :rfc:`5011` trust anchor maintenance is not used with either of the keys + involved.) + +Timing Options +~~~~~~~~~~~~~~ + +Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS +(which is the format used inside key files), +or 'Day Mon DD HH:MM:SS YYYY' (as printed by ``dnssec-settime -p``), +or UNIX epoch time (as printed by ``dnssec-settime -up``), +or the literal ``now``. + +The argument can be followed by ``+`` or ``-`` and an offset from the +given time. The literal ``now`` can be omitted before an offset. The +offset can be followed by one of the suffixes ``y``, ``mo``, ``w``, +``d``, ``h``, or ``mi``, so that it is computed in years (defined as +365 24-hour days, ignoring leap years), months (defined as 30 24-hour +days), weeks, days, hours, or minutes, respectively. Without a suffix, +the offset is computed in seconds. + +To explicitly prevent a date from being set, use ``none``, ``never``, +or ``unset``. + +All these formats are case-insensitive. + +.. option:: -P date/offset + + This option sets the date on which a key is to be published to the zone. After + that date, the key is included in the zone but is not used + to sign it. If not set, and if the :option:`-G` option has not been used, the + default is the current date. + + .. program:: dnssec-keyfromlabel -P + .. option:: sync date/offset + + This option sets the date on which CDS and CDNSKEY records that match this key + are to be published to the zone. + +.. program:: dnssec-keyfromlabel + +.. option:: -A date/offset + + This option sets the date on which the key is to be activated. After that date, + the key is included in the zone and used to sign it. If not set, + and if the :option:`-G` option has not been used, the default is the current date. + +.. option:: -R date/offset + + This option sets the date on which the key is to be revoked. After that date, the + key is flagged as revoked. It is included in the zone and + is used to sign it. + +.. option:: -I date/offset + + This option sets the date on which the key is to be retired. After that date, the + key is still included in the zone, but it is not used to + sign it. + +.. option:: -D date/offset + + This option sets the date on which the key is to be deleted. After that date, the + key is no longer included in the zone. (However, it may remain in the key + repository.) + + .. program:: dnssec-keyfromlabel -D + .. option:: sync date/offset + + This option sets the date on which the CDS and CDNSKEY records that match this + key are to be deleted. + +.. program:: dnssec-keyfromlabel + +.. option:: -i interval + + This option sets the prepublication interval for a key. If set, then the + publication and activation dates must be separated by at least this + much time. If the activation date is specified but the publication + date is not, the publication date defaults to this much time + before the activation date; conversely, if the publication date is + specified but not the activation date, activation is set to + this much time after publication. + + If the key is being created as an explicit successor to another key, + then the default prepublication interval is 30 days; otherwise it is + zero. + + As with date offsets, if the argument is followed by one of the + suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is + measured in years, months, weeks, days, hours, or minutes, + respectively. Without a suffix, the interval is measured in seconds. + +Generated Key Files +~~~~~~~~~~~~~~~~~~~ + +When :program:`dnssec-keyfromlabel` completes successfully, it prints a string +of the form ``Knnnn.+aaa+iiiii`` to the standard output. This is an +identification string for the key files it has generated. + +- ``nnnn`` is the key name. + +- ``aaa`` is the numeric representation of the algorithm. + +- ``iiiii`` is the key identifier (or footprint). + +:program:`dnssec-keyfromlabel` creates two files, with names based on the +printed string. ``Knnnn.+aaa+iiiii.key`` contains the public key, and +``Knnnn.+aaa+iiiii.private`` contains the private key. + +The ``.key`` file contains a DNS KEY record that can be inserted into a +zone file (directly or with an $INCLUDE statement). + +The ``.private`` file contains algorithm-specific fields. For obvious +security reasons, this file does not have general read permission. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, :iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, +:rfc:`4034`, :rfc:`7512`. diff --git a/bin/dnssec/dnssec-keygen.c b/bin/dnssec/dnssec-keygen.c new file mode 100644 index 0000000..3d77976 --- /dev/null +++ b/bin/dnssec/dnssec-keygen.c @@ -0,0 +1,1292 @@ +/* + * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + * + * Portions Copyright (C) Network Associates, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "dnssectool.h" + +#define MAX_RSA 4096 /* should be long enough... */ + +const char *program = "dnssec-keygen"; + +isc_log_t *lctx = NULL; + +noreturn static void +usage(void); + +static void +progress(int p); + +struct keygen_ctx { + const char *predecessor; + const char *policy; + const char *configfile; + const char *directory; + char *algname; + char *nametype; + char *type; + int generator; + int protocol; + int size; + int signatory; + dns_rdataclass_t rdclass; + int options; + int dbits; + dns_ttl_t ttl; + uint16_t kskflag; + uint16_t revflag; + dns_secalg_t alg; + /* timing data */ + int prepub; + isc_stdtime_t now; + isc_stdtime_t publish; + isc_stdtime_t activate; + isc_stdtime_t inactive; + isc_stdtime_t revokekey; + isc_stdtime_t deltime; + isc_stdtime_t syncadd; + isc_stdtime_t syncdel; + bool setpub; + bool setact; + bool setinact; + bool setrev; + bool setdel; + bool setsyncadd; + bool setsyncdel; + bool unsetpub; + bool unsetact; + bool unsetinact; + bool unsetrev; + bool unsetdel; + /* how to generate the key */ + bool setttl; + bool use_nsec3; + bool genonly; + bool showprogress; + bool quiet; + bool oldstyle; + /* state */ + time_t lifetime; + bool ksk; + bool zsk; +}; + +typedef struct keygen_ctx keygen_ctx_t; + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options] name\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, " name: owner of the key\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -K : write keys into directory\n"); + fprintf(stderr, " -k : generate keys for dnssec-policy\n"); + fprintf(stderr, " -l : configuration file with dnssec-policy " + "statement\n"); + fprintf(stderr, " -a :\n"); + fprintf(stderr, " RSASHA1 | NSEC3RSASHA1 |\n"); + fprintf(stderr, " RSASHA256 | RSASHA512 |\n"); + fprintf(stderr, " ECDSAP256SHA256 | ECDSAP384SHA384 |\n"); + fprintf(stderr, " ED25519 | ED448 | DH\n"); + fprintf(stderr, " -3: use NSEC3-capable algorithm\n"); + fprintf(stderr, " -b :\n"); + fprintf(stderr, " RSASHA1:\t[1024..%d]\n", MAX_RSA); + fprintf(stderr, " NSEC3RSASHA1:\t[1024..%d]\n", MAX_RSA); + fprintf(stderr, " RSASHA256:\t[1024..%d]\n", MAX_RSA); + fprintf(stderr, " RSASHA512:\t[1024..%d]\n", MAX_RSA); + fprintf(stderr, " DH:\t\t[128..4096]\n"); + fprintf(stderr, " ECDSAP256SHA256:\tignored\n"); + fprintf(stderr, " ECDSAP384SHA384:\tignored\n"); + fprintf(stderr, " ED25519:\tignored\n"); + fprintf(stderr, " ED448:\tignored\n"); + fprintf(stderr, " (key size defaults are set according to\n" + " algorithm and usage (ZSK or KSK)\n"); + fprintf(stderr, " -n : ZONE | HOST | ENTITY | " + "USER | OTHER\n"); + fprintf(stderr, " (DNSKEY generation defaults to ZONE)\n"); + fprintf(stderr, " -c : (default: IN)\n"); + fprintf(stderr, " -d (0 => max, default)\n"); + fprintf(stderr, " -E :\n"); + fprintf(stderr, " name of an OpenSSL engine to use\n"); + fprintf(stderr, " -f : KSK | REVOKE\n"); + fprintf(stderr, " -g : use specified generator " + "(DH only)\n"); + fprintf(stderr, " -L : default key TTL\n"); + fprintf(stderr, " -p : (default: 3 [dnssec])\n"); + fprintf(stderr, " -s : strength value this key signs DNS " + "records with (default: 0)\n"); + fprintf(stderr, " -T : DNSKEY | KEY (default: DNSKEY; " + "use KEY for SIG(0))\n"); + fprintf(stderr, " -t : " + "AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF " + "(default: AUTHCONF)\n"); + fprintf(stderr, " -h: print usage and exit\n"); + fprintf(stderr, " -m :\n"); + fprintf(stderr, " usage | trace | record | size | mctx\n"); + fprintf(stderr, " -v : set verbosity level (0 - 10)\n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, "Timing options:\n"); + fprintf(stderr, " -P date/[+-]offset/none: set key publication date " + "(default: now)\n"); + fprintf(stderr, " -P sync date/[+-]offset/none: set CDS and CDNSKEY " + "publication date\n"); + fprintf(stderr, " -A date/[+-]offset/none: set key activation date " + "(default: now)\n"); + fprintf(stderr, " -R date/[+-]offset/none: set key " + "revocation date\n"); + fprintf(stderr, " -I date/[+-]offset/none: set key " + "inactivation date\n"); + fprintf(stderr, " -D date/[+-]offset/none: set key deletion date\n"); + fprintf(stderr, " -D sync date/[+-]offset/none: set CDS and CDNSKEY " + "deletion date\n"); + + fprintf(stderr, " -G: generate key only; do not set -P or -A\n"); + fprintf(stderr, " -C: generate a backward-compatible key, omitting " + "all dates\n"); + fprintf(stderr, " -S : generate a successor to an existing " + "key\n"); + fprintf(stderr, " -i : prepublication interval for " + "successor key " + "(default: 30 days)\n"); + fprintf(stderr, "Output:\n"); + fprintf(stderr, " K++.key, " + "K++.private\n"); + + exit(-1); +} + +static void +progress(int p) { + char c = '*'; + + switch (p) { + case 0: + c = '.'; + break; + case 1: + c = '+'; + break; + case 2: + c = '*'; + break; + case 3: + c = ' '; + break; + default: + break; + } + (void)putc(c, stderr); + (void)fflush(stderr); +} + +static void +kasp_from_conf(cfg_obj_t *config, isc_mem_t *mctx, const char *name, + dns_kasp_t **kaspp) { + const cfg_listelt_t *element; + const cfg_obj_t *kasps = NULL; + dns_kasp_t *kasp = NULL, *kasp_next; + isc_result_t result = ISC_R_NOTFOUND; + dns_kasplist_t kasplist; + + ISC_LIST_INIT(kasplist); + + (void)cfg_map_get(config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + kasp = NULL; + if (strcmp(cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + name) != 0) + { + continue; + } + + result = cfg_kasp_fromconfig(kconfig, NULL, mctx, lctx, + &kasplist, &kasp); + if (result != ISC_R_SUCCESS) { + fatal("failed to configure dnssec-policy '%s': %s", + cfg_obj_asstring(cfg_tuple_get(kconfig, "name")), + isc_result_totext(result)); + } + INSIST(kasp != NULL); + dns_kasp_freeze(kasp); + break; + } + + *kaspp = kasp; + + /* + * Cleanup kasp list. + */ + for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(kasplist, kasp, link); + dns_kasp_detach(&kasp); + } +} + +static void +keygen(keygen_ctx_t *ctx, isc_mem_t *mctx, int argc, char **argv) { + char filename[255]; + char algstr[DNS_SECALG_FORMATSIZE]; + uint16_t flags = 0; + int param = 0; + bool null_key = false; + bool conflict = false; + bool show_progress = false; + isc_buffer_t buf; + dns_name_t *name; + dns_fixedname_t fname; + isc_result_t ret; + dst_key_t *key = NULL; + dst_key_t *prevkey = NULL; + + UNUSED(argc); + + dns_secalg_format(ctx->alg, algstr, sizeof(algstr)); + + if (ctx->predecessor == NULL) { + if (ctx->prepub == -1) { + ctx->prepub = 0; + } + + name = dns_fixedname_initname(&fname); + isc_buffer_init(&buf, argv[isc_commandline_index], + strlen(argv[isc_commandline_index])); + isc_buffer_add(&buf, strlen(argv[isc_commandline_index])); + ret = dns_name_fromtext(name, &buf, dns_rootname, 0, NULL); + if (ret != ISC_R_SUCCESS) { + fatal("invalid key name %s: %s", + argv[isc_commandline_index], + isc_result_totext(ret)); + } + + if (!dst_algorithm_supported(ctx->alg)) { + fatal("unsupported algorithm: %s", algstr); + } + + if (ctx->alg == DST_ALG_DH) { + ctx->options |= DST_TYPE_KEY; + } + + if (ctx->use_nsec3) { + switch (ctx->alg) { + case DST_ALG_RSASHA1: + ctx->alg = DST_ALG_NSEC3RSASHA1; + break; + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + case DST_ALG_ECDSA256: + case DST_ALG_ECDSA384: + case DST_ALG_ED25519: + case DST_ALG_ED448: + break; + default: + fatal("algorithm %s is incompatible with NSEC3" + ", do not use the -3 option", + algstr); + } + } + + if (ctx->type != NULL && (ctx->options & DST_TYPE_KEY) != 0) { + if (strcasecmp(ctx->type, "NOAUTH") == 0) { + flags |= DNS_KEYTYPE_NOAUTH; + } else if (strcasecmp(ctx->type, "NOCONF") == 0) { + flags |= DNS_KEYTYPE_NOCONF; + } else if (strcasecmp(ctx->type, "NOAUTHCONF") == 0) { + flags |= (DNS_KEYTYPE_NOAUTH | + DNS_KEYTYPE_NOCONF); + if (ctx->size < 0) { + ctx->size = 0; + } + } else if (strcasecmp(ctx->type, "AUTHCONF") == 0) { + /* nothing */ + } else { + fatal("invalid type %s", ctx->type); + } + } + + if (ctx->size < 0) { + switch (ctx->alg) { + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + case DST_ALG_RSASHA256: + case DST_ALG_RSASHA512: + ctx->size = 2048; + if (verbose > 0) { + fprintf(stderr, + "key size not " + "specified; defaulting" + " to %d\n", + ctx->size); + } + break; + case DST_ALG_ECDSA256: + case DST_ALG_ECDSA384: + case DST_ALG_ED25519: + case DST_ALG_ED448: + break; + default: + fatal("key size not specified (-b option)"); + } + } + + if (!ctx->oldstyle && ctx->prepub > 0) { + if (ctx->setpub && ctx->setact && + (ctx->activate - ctx->prepub) < ctx->publish) + { + fatal("Activation and publication dates " + "are closer together than the\n\t" + "prepublication interval."); + } + + if (!ctx->setpub && !ctx->setact) { + ctx->setpub = ctx->setact = true; + ctx->publish = ctx->now; + ctx->activate = ctx->now + ctx->prepub; + } else if (ctx->setpub && !ctx->setact) { + ctx->setact = true; + ctx->activate = ctx->publish + ctx->prepub; + } else if (ctx->setact && !ctx->setpub) { + ctx->setpub = true; + ctx->publish = ctx->activate - ctx->prepub; + } + + if ((ctx->activate - ctx->prepub) < ctx->now) { + fatal("Time until activation is shorter " + "than the\n\tprepublication interval."); + } + } + } else { + char keystr[DST_KEY_FORMATSIZE]; + isc_stdtime_t when; + int major, minor; + + if (ctx->prepub == -1) { + ctx->prepub = (30 * 86400); + } + + if (ctx->alg != 0) { + fatal("-S and -a cannot be used together"); + } + if (ctx->size >= 0) { + fatal("-S and -b cannot be used together"); + } + if (ctx->nametype != NULL) { + fatal("-S and -n cannot be used together"); + } + if (ctx->type != NULL) { + fatal("-S and -t cannot be used together"); + } + if (ctx->setpub || ctx->unsetpub) { + fatal("-S and -P cannot be used together"); + } + if (ctx->setact || ctx->unsetact) { + fatal("-S and -A cannot be used together"); + } + if (ctx->use_nsec3) { + fatal("-S and -3 cannot be used together"); + } + if (ctx->oldstyle) { + fatal("-S and -C cannot be used together"); + } + if (ctx->genonly) { + fatal("-S and -G cannot be used together"); + } + + ret = dst_key_fromnamedfile( + ctx->predecessor, ctx->directory, + (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE), + mctx, &prevkey); + if (ret != ISC_R_SUCCESS) { + fatal("Invalid keyfile %s: %s", ctx->predecessor, + isc_result_totext(ret)); + } + if (!dst_key_isprivate(prevkey)) { + fatal("%s is not a private key", ctx->predecessor); + } + + name = dst_key_name(prevkey); + ctx->alg = dst_key_alg(prevkey); + ctx->size = dst_key_size(prevkey); + flags = dst_key_flags(prevkey); + + dst_key_format(prevkey, keystr, sizeof(keystr)); + dst_key_getprivateformat(prevkey, &major, &minor); + if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { + fatal("Key %s has incompatible format version %d.%d\n\t" + "It is not possible to generate a successor key.", + keystr, major, minor); + } + + ret = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &when); + if (ret != ISC_R_SUCCESS) { + fatal("Key %s has no activation date.\n\t" + "You must use dnssec-settime -A to set one " + "before generating a successor.", + keystr); + } + + ret = dst_key_gettime(prevkey, DST_TIME_INACTIVE, + &ctx->activate); + if (ret != ISC_R_SUCCESS) { + fatal("Key %s has no inactivation date.\n\t" + "You must use dnssec-settime -I to set one " + "before generating a successor.", + keystr); + } + + ctx->publish = ctx->activate - ctx->prepub; + if (ctx->publish < ctx->now) { + fatal("Key %s becomes inactive\n\t" + "sooner than the prepublication period " + "for the new key ends.\n\t" + "Either change the inactivation date with " + "dnssec-settime -I,\n\t" + "or use the -i option to set a shorter " + "prepublication interval.", + keystr); + } + + ret = dst_key_gettime(prevkey, DST_TIME_DELETE, &when); + if (ret != ISC_R_SUCCESS) { + fprintf(stderr, + "%s: WARNING: Key %s has no removal " + "date;\n\t it will remain in the zone " + "indefinitely after rollover.\n\t " + "You can use dnssec-settime -D to " + "change this.\n", + program, keystr); + } + + ctx->setpub = ctx->setact = true; + } + + switch (ctx->alg) { + case DNS_KEYALG_RSASHA1: + case DNS_KEYALG_NSEC3RSASHA1: + case DNS_KEYALG_RSASHA256: + if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA)) + { + fatal("RSA key size %d out of range", ctx->size); + } + break; + case DNS_KEYALG_RSASHA512: + if (ctx->size != 0 && (ctx->size < 1024 || ctx->size > MAX_RSA)) + { + fatal("RSA key size %d out of range", ctx->size); + } + break; + case DNS_KEYALG_DH: + if (ctx->size != 0 && (ctx->size < 128 || ctx->size > 4096)) { + fatal("DH key size %d out of range", ctx->size); + } + break; + case DST_ALG_ECDSA256: + ctx->size = 256; + break; + case DST_ALG_ECDSA384: + ctx->size = 384; + break; + case DST_ALG_ED25519: + ctx->size = 256; + break; + case DST_ALG_ED448: + ctx->size = 456; + break; + } + + if (ctx->alg != DNS_KEYALG_DH && ctx->generator != 0) { + fatal("specified DH generator for a non-DH key"); + } + + if (ctx->nametype == NULL) { + if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ + fatal("no nametype specified"); + } + flags |= DNS_KEYOWNER_ZONE; /* DNSKEY */ + } else if (strcasecmp(ctx->nametype, "zone") == 0) { + flags |= DNS_KEYOWNER_ZONE; + } else if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ + if (strcasecmp(ctx->nametype, "host") == 0 || + strcasecmp(ctx->nametype, "entity") == 0) + { + flags |= DNS_KEYOWNER_ENTITY; + } else if (strcasecmp(ctx->nametype, "user") == 0) { + flags |= DNS_KEYOWNER_USER; + } else { + fatal("invalid KEY nametype %s", ctx->nametype); + } + } else if (strcasecmp(ctx->nametype, "other") != 0) { /* DNSKEY */ + fatal("invalid DNSKEY nametype %s", ctx->nametype); + } + + if (ctx->directory == NULL) { + ctx->directory = "."; + } + + if ((ctx->options & DST_TYPE_KEY) != 0) { /* KEY */ + flags |= ctx->signatory; + } else if ((flags & DNS_KEYOWNER_ZONE) != 0) { /* DNSKEY */ + flags |= ctx->kskflag; + flags |= ctx->revflag; + } + + if (ctx->protocol == -1) { + ctx->protocol = DNS_KEYPROTO_DNSSEC; + } else if ((ctx->options & DST_TYPE_KEY) == 0 && + ctx->protocol != DNS_KEYPROTO_DNSSEC) + { + fatal("invalid DNSKEY protocol: %d", ctx->protocol); + } + + if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { + if (ctx->size > 0) { + fatal("specified null key with non-zero size"); + } + if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0) { + fatal("specified null key with signing authority"); + } + } + + if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE && + ctx->alg == DNS_KEYALG_DH) + { + fatal("a key with algorithm %s cannot be a zone key", algstr); + } + + switch (ctx->alg) { + case DNS_KEYALG_RSASHA1: + case DNS_KEYALG_NSEC3RSASHA1: + case DNS_KEYALG_RSASHA256: + case DNS_KEYALG_RSASHA512: + show_progress = true; + break; + + case DNS_KEYALG_DH: + param = ctx->generator; + break; + + case DST_ALG_ECDSA256: + case DST_ALG_ECDSA384: + case DST_ALG_ED25519: + case DST_ALG_ED448: + show_progress = true; + break; + } + + if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { + null_key = true; + } + + isc_buffer_init(&buf, filename, sizeof(filename) - 1); + + do { + conflict = false; + + if (!ctx->quiet && show_progress) { + fprintf(stderr, "Generating key pair."); + ret = dst_key_generate(name, ctx->alg, ctx->size, param, + flags, ctx->protocol, + ctx->rdclass, mctx, &key, + &progress); + putc('\n', stderr); + fflush(stderr); + } else { + ret = dst_key_generate(name, ctx->alg, ctx->size, param, + flags, ctx->protocol, + ctx->rdclass, mctx, &key, NULL); + } + + if (ret != ISC_R_SUCCESS) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namestr, sizeof(namestr)); + fatal("failed to generate key %s/%s: %s\n", namestr, + algstr, isc_result_totext(ret)); + } + + dst_key_setbits(key, ctx->dbits); + + /* + * Set key timing metadata (unless using -C) + * + * Creation date is always set to "now". + * + * For a new key without an explicit predecessor, publish + * and activation dates are set to "now" by default, but + * can both be overridden. + * + * For a successor key, activation is set to match the + * predecessor's inactivation date. Publish is set to 30 + * days earlier than that (XXX: this should be configurable). + * If either of the resulting dates are in the past, that's + * an error; the inactivation date of the predecessor key + * must be updated before a successor key can be created. + */ + if (!ctx->oldstyle) { + dst_key_settime(key, DST_TIME_CREATED, ctx->now); + + if (ctx->genonly && (ctx->setpub || ctx->setact)) { + fatal("cannot use -G together with " + "-P or -A options"); + } + + if (ctx->setpub) { + dst_key_settime(key, DST_TIME_PUBLISH, + ctx->publish); + } else if (ctx->setact && !ctx->unsetpub) { + dst_key_settime(key, DST_TIME_PUBLISH, + ctx->activate - ctx->prepub); + } else if (!ctx->genonly && !ctx->unsetpub) { + dst_key_settime(key, DST_TIME_PUBLISH, + ctx->now); + } + + if (ctx->setact) { + dst_key_settime(key, DST_TIME_ACTIVATE, + ctx->activate); + } else if (!ctx->genonly && !ctx->unsetact) { + dst_key_settime(key, DST_TIME_ACTIVATE, + ctx->now); + } + + if (ctx->setrev) { + if (ctx->kskflag == 0) { + fprintf(stderr, + "%s: warning: Key is " + "not flagged as a KSK, but -R " + "was used. Revoking a ZSK is " + "legal, but undefined.\n", + program); + } + dst_key_settime(key, DST_TIME_REVOKE, + ctx->revokekey); + } + + if (ctx->setinact) { + dst_key_settime(key, DST_TIME_INACTIVE, + ctx->inactive); + } + + if (ctx->setdel) { + if (ctx->setinact && + ctx->deltime < ctx->inactive) + { + fprintf(stderr, + "%s: warning: Key is " + "scheduled to be deleted " + "before it is scheduled to be " + "made inactive.\n", + program); + } + dst_key_settime(key, DST_TIME_DELETE, + ctx->deltime); + } + + if (ctx->setsyncadd) { + dst_key_settime(key, DST_TIME_SYNCPUBLISH, + ctx->syncadd); + } + + if (ctx->setsyncdel) { + dst_key_settime(key, DST_TIME_SYNCDELETE, + ctx->syncdel); + } + } else { + if (ctx->setpub || ctx->setact || ctx->setrev || + ctx->setinact || ctx->setdel || ctx->unsetpub || + ctx->unsetact || ctx->unsetrev || ctx->unsetinact || + ctx->unsetdel || ctx->genonly || ctx->setsyncadd || + ctx->setsyncdel) + { + fatal("cannot use -C together with " + "-P, -A, -R, -I, -D, or -G options"); + } + /* + * Compatibility mode: Private-key-format + * should be set to 1.2. + */ + dst_key_setprivateformat(key, 1, 2); + } + + /* Set the default key TTL */ + if (ctx->setttl) { + dst_key_setttl(key, ctx->ttl); + } + + /* Set dnssec-policy related metadata */ + if (ctx->policy != NULL) { + dst_key_setnum(key, DST_NUM_LIFETIME, ctx->lifetime); + dst_key_setbool(key, DST_BOOL_KSK, ctx->ksk); + dst_key_setbool(key, DST_BOOL_ZSK, ctx->zsk); + } + + /* + * Do not overwrite an existing key, or create a key + * if there is a risk of ID collision due to this key + * or another key being revoked. + */ + if (key_collision(key, name, ctx->directory, mctx, NULL)) { + conflict = true; + if (null_key) { + dst_key_free(&key); + break; + } + + if (verbose > 0) { + isc_buffer_clear(&buf); + ret = dst_key_buildfilename( + key, 0, ctx->directory, &buf); + if (ret == ISC_R_SUCCESS) { + fprintf(stderr, + "%s: %s already exists, or " + "might collide with another " + "key upon revokation. " + "Generating a new key\n", + program, filename); + } + } + + dst_key_free(&key); + } + } while (conflict); + + if (conflict) { + fatal("cannot generate a null key due to possible key ID " + "collision"); + } + + if (ctx->predecessor != NULL && prevkey != NULL) { + dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key)); + dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey)); + + ret = dst_key_tofile(prevkey, ctx->options, ctx->directory); + if (ret != ISC_R_SUCCESS) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(prevkey, keystr, sizeof(keystr)); + fatal("failed to update predecessor %s: %s\n", keystr, + isc_result_totext(ret)); + } + } + + ret = dst_key_tofile(key, ctx->options, ctx->directory); + if (ret != ISC_R_SUCCESS) { + char keystr[DST_KEY_FORMATSIZE]; + dst_key_format(key, keystr, sizeof(keystr)); + fatal("failed to write key %s: %s\n", keystr, + isc_result_totext(ret)); + } + + isc_buffer_clear(&buf); + ret = dst_key_buildfilename(key, 0, NULL, &buf); + if (ret != ISC_R_SUCCESS) { + fatal("dst_key_buildfilename returned: %s\n", + isc_result_totext(ret)); + } + printf("%s\n", filename); + + dst_key_free(&key); + if (prevkey != NULL) { + dst_key_free(&prevkey); + } +} + +int +main(int argc, char **argv) { + char *algname = NULL, *freeit = NULL; + char *classname = NULL; + char *endp; + isc_mem_t *mctx = NULL; + isc_result_t ret; + isc_textregion_t r; + const char *engine = NULL; + unsigned char c; + int ch; + + keygen_ctx_t ctx = { + .options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC, + .prepub = -1, + .protocol = -1, + .size = -1, + }; + + if (argc == 1) { + usage(); + } + + isc_commandline_errprint = false; + + /* + * Process memory debugging argument first. + */ +#define CMDLINE_FLAGS \ + "3A:a:b:Cc:D:d:E:eFf:Gg:hI:i:K:k:L:l:m:n:P:p:qR:r:S:s:" \ + "T:t:v:V" + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'm': + if (strcasecmp(isc_commandline_argument, "record") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } + if (strcasecmp(isc_commandline_argument, "trace") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } + if (strcasecmp(isc_commandline_argument, "usage") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + default: + break; + } + } + isc_commandline_reset = true; + + isc_mem_create(&mctx); + isc_stdtime_get(&ctx.now); + + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case '3': + ctx.use_nsec3 = true; + break; + case 'a': + algname = isc_commandline_argument; + break; + case 'b': + ctx.size = strtol(isc_commandline_argument, &endp, 10); + if (*endp != '\0' || ctx.size < 0) { + fatal("-b requires a non-negative number"); + } + break; + case 'C': + ctx.oldstyle = true; + break; + case 'c': + classname = isc_commandline_argument; + break; + case 'd': + ctx.dbits = strtol(isc_commandline_argument, &endp, 10); + if (*endp != '\0' || ctx.dbits < 0) { + fatal("-d requires a non-negative number"); + } + break; + case 'E': + engine = isc_commandline_argument; + break; + case 'e': + fprintf(stderr, "phased-out option -e " + "(was 'use (RSA) large exponent')\n"); + break; + case 'f': + c = (unsigned char)(isc_commandline_argument[0]); + if (toupper(c) == 'K') { + ctx.kskflag = DNS_KEYFLAG_KSK; + } else if (toupper(c) == 'R') { + ctx.revflag = DNS_KEYFLAG_REVOKE; + } else { + fatal("unknown flag '%s'", + isc_commandline_argument); + } + break; + case 'g': + ctx.generator = strtol(isc_commandline_argument, &endp, + 10); + if (*endp != '\0' || ctx.generator <= 0) { + fatal("-g requires a positive number"); + } + break; + case 'K': + ctx.directory = isc_commandline_argument; + ret = try_dir(ctx.directory); + if (ret != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", + ctx.directory, isc_result_totext(ret)); + } + break; + case 'k': + ctx.policy = isc_commandline_argument; + break; + case 'L': + ctx.ttl = strtottl(isc_commandline_argument); + ctx.setttl = true; + break; + case 'l': + ctx.configfile = isc_commandline_argument; + break; + case 'n': + ctx.nametype = isc_commandline_argument; + break; + case 'm': + break; + case 'p': + ctx.protocol = strtol(isc_commandline_argument, &endp, + 10); + if (*endp != '\0' || ctx.protocol < 0 || + ctx.protocol > 255) + { + fatal("-p must be followed by a number " + "[0..255]"); + } + break; + case 'q': + ctx.quiet = true; + break; + case 'r': + fatal("The -r option has been deprecated.\n" + "System random data is always used.\n"); + break; + case 's': + ctx.signatory = strtol(isc_commandline_argument, &endp, + 10); + if (*endp != '\0' || ctx.signatory < 0 || + ctx.signatory > 15) + { + fatal("-s must be followed by a number " + "[0..15]"); + } + break; + case 'T': + if (strcasecmp(isc_commandline_argument, "KEY") == 0) { + ctx.options |= DST_TYPE_KEY; + } else if (strcasecmp(isc_commandline_argument, + "DNSKE" + "Y") == 0) + { + /* default behavior */ + } else { + fatal("unknown type '%s'", + isc_commandline_argument); + } + break; + case 't': + ctx.type = isc_commandline_argument; + break; + case 'v': + endp = NULL; + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case 'G': + ctx.genonly = true; + break; + case 'P': + /* -Psync ? */ + if (isoptarg("sync", argv, usage)) { + if (ctx.setsyncadd) { + fatal("-P sync specified more than " + "once"); + } + + ctx.syncadd = strtotime( + isc_commandline_argument, ctx.now, + ctx.now, &ctx.setsyncadd); + break; + } + (void)isoptarg("dnskey", argv, usage); + if (ctx.setpub || ctx.unsetpub) { + fatal("-P specified more than once"); + } + + ctx.publish = strtotime(isc_commandline_argument, + ctx.now, ctx.now, &ctx.setpub); + ctx.unsetpub = !ctx.setpub; + break; + case 'A': + if (ctx.setact || ctx.unsetact) { + fatal("-A specified more than once"); + } + + ctx.activate = strtotime(isc_commandline_argument, + ctx.now, ctx.now, &ctx.setact); + ctx.unsetact = !ctx.setact; + break; + case 'R': + if (ctx.setrev || ctx.unsetrev) { + fatal("-R specified more than once"); + } + + ctx.revokekey = strtotime(isc_commandline_argument, + ctx.now, ctx.now, + &ctx.setrev); + ctx.unsetrev = !ctx.setrev; + break; + case 'I': + if (ctx.setinact || ctx.unsetinact) { + fatal("-I specified more than once"); + } + + ctx.inactive = strtotime(isc_commandline_argument, + ctx.now, ctx.now, + &ctx.setinact); + ctx.unsetinact = !ctx.setinact; + break; + case 'D': + /* -Dsync ? */ + if (isoptarg("sync", argv, usage)) { + if (ctx.setsyncdel) { + fatal("-D sync specified more than " + "once"); + } + + ctx.syncdel = strtotime( + isc_commandline_argument, ctx.now, + ctx.now, &ctx.setsyncdel); + break; + } + (void)isoptarg("dnskey", argv, usage); + if (ctx.setdel || ctx.unsetdel) { + fatal("-D specified more than once"); + } + + ctx.deltime = strtotime(isc_commandline_argument, + ctx.now, ctx.now, &ctx.setdel); + ctx.unsetdel = !ctx.setdel; + break; + case 'S': + ctx.predecessor = isc_commandline_argument; + break; + case 'i': + ctx.prepub = strtottl(isc_commandline_argument); + break; + case 'F': + /* Reserved for FIPS mode */ + FALLTHROUGH; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + if (!isatty(0)) { + ctx.quiet = true; + } + + ret = dst_lib_init(mctx, engine); + if (ret != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", isc_result_totext(ret)); + } + + setup_logging(mctx, &lctx); + + ctx.rdclass = strtoclass(classname); + + if (ctx.configfile == NULL || ctx.configfile[0] == '\0') { + ctx.configfile = NAMED_CONFFILE; + } + + if (ctx.predecessor == NULL) { + if (argc < isc_commandline_index + 1) { + fatal("the key name was not specified"); + } + if (argc > isc_commandline_index + 1) { + fatal("extraneous arguments"); + } + } + + if (ctx.predecessor == NULL && ctx.policy == NULL) { + if (algname == NULL) { + fatal("no algorithm specified"); + } + r.base = algname; + r.length = strlen(algname); + ret = dns_secalg_fromtext(&ctx.alg, &r); + if (ret != ISC_R_SUCCESS) { + fatal("unknown algorithm %s", algname); + } + if (!dst_algorithm_supported(ctx.alg)) { + fatal("unsupported algorithm: %s", algname); + } + } + + if (ctx.policy != NULL) { + if (ctx.nametype != NULL) { + fatal("-k and -n cannot be used together"); + } + if (ctx.predecessor != NULL) { + fatal("-k and -S cannot be used together"); + } + if (ctx.oldstyle) { + fatal("-k and -C cannot be used together"); + } + if (ctx.setttl) { + fatal("-k and -L cannot be used together"); + } + if (ctx.prepub > 0) { + fatal("-k and -i cannot be used together"); + } + if (ctx.size != -1) { + fatal("-k and -b cannot be used together"); + } + if (ctx.kskflag || ctx.revflag) { + fatal("-k and -f cannot be used together"); + } + if (ctx.options & DST_TYPE_KEY) { + fatal("-k and -T KEY cannot be used together"); + } + if (ctx.use_nsec3) { + fatal("-k and -3 cannot be used together"); + } + + ctx.options |= DST_TYPE_STATE; + + if (strcmp(ctx.policy, "default") == 0) { + ctx.use_nsec3 = false; + ctx.alg = DST_ALG_ECDSA256; + ctx.size = 0; + ctx.kskflag = DNS_KEYFLAG_KSK; + ctx.ttl = 3600; + ctx.setttl = true; + ctx.ksk = true; + ctx.zsk = true; + ctx.lifetime = 0; + + keygen(&ctx, mctx, argc, argv); + } else { + cfg_parser_t *parser = NULL; + cfg_obj_t *config = NULL; + dns_kasp_t *kasp = NULL; + dns_kasp_key_t *kaspkey = NULL; + + RUNTIME_CHECK(cfg_parser_create(mctx, lctx, &parser) == + ISC_R_SUCCESS); + if (cfg_parse_file(parser, ctx.configfile, + &cfg_type_namedconf, + &config) != ISC_R_SUCCESS) + { + fatal("unable to load dnssec-policy '%s' from " + "'%s'", + ctx.policy, ctx.configfile); + } + + kasp_from_conf(config, mctx, ctx.policy, &kasp); + if (kasp == NULL) { + fatal("failed to load dnssec-policy '%s'", + ctx.policy); + } + if (ISC_LIST_EMPTY(dns_kasp_keys(kasp))) { + fatal("dnssec-policy '%s' has no keys " + "configured", + ctx.policy); + } + + ctx.ttl = dns_kasp_dnskeyttl(kasp); + ctx.setttl = true; + + kaspkey = ISC_LIST_HEAD(dns_kasp_keys(kasp)); + + while (kaspkey != NULL) { + ctx.use_nsec3 = false; + ctx.alg = dns_kasp_key_algorithm(kaspkey); + ctx.size = dns_kasp_key_size(kaspkey); + ctx.kskflag = dns_kasp_key_ksk(kaspkey) + ? DNS_KEYFLAG_KSK + : 0; + ctx.ksk = dns_kasp_key_ksk(kaspkey); + ctx.zsk = dns_kasp_key_zsk(kaspkey); + ctx.lifetime = dns_kasp_key_lifetime(kaspkey); + + keygen(&ctx, mctx, argc, argv); + + kaspkey = ISC_LIST_NEXT(kaspkey, link); + } + + dns_kasp_detach(&kasp); + cfg_obj_destroy(parser, &config); + cfg_parser_destroy(&parser); + } + } else { + keygen(&ctx, mctx, argc, argv); + } + + cleanup_logging(&lctx); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + + if (freeit != NULL) { + free(freeit); + } + + return (0); +} diff --git a/bin/dnssec/dnssec-keygen.rst b/bin/dnssec/dnssec-keygen.rst new file mode 100644 index 0000000..a06027c --- /dev/null +++ b/bin/dnssec/dnssec-keygen.rst @@ -0,0 +1,357 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-keygen +.. program:: dnssec-keygen +.. _man_dnssec-keygen: + +dnssec-keygen: DNSSEC key generation tool +----------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-keygen` [**-3**] [**-A** date/offset] [**-a** algorithm] [**-b** keysize] [**-C**] [**-c** class] [**-D** date/offset] [**-d** bits] [**-D** sync date/offset] [**-E** engine] [**-f** flag] [**-G**] [**-g** generator] [**-h**] [**-I** date/offset] [**-i** interval] [**-K** directory] [**-k** policy] [**-L** ttl] [**-l** file] [**-n** nametype] [**-P** date/offset] [**-P** sync date/offset] [**-p** protocol] [**-q**] [**-R** date/offset] [**-S** key] [**-s** strength] [**-T** rrtype] [**-t** type] [**-V**] [**-v** level] {name} + +Description +~~~~~~~~~~~ + +:program:`dnssec-keygen` generates keys for DNSSEC (Secure DNS), as defined in +:rfc:`2535` and :rfc:`4034`. It can also generate keys for use with TSIG +(Transaction Signatures) as defined in :rfc:`2845`, or TKEY (Transaction +Key) as defined in :rfc:`2930`. + +The ``name`` of the key is specified on the command line. For DNSSEC +keys, this must match the name of the zone for which the key is being +generated. + +Options +~~~~~~~ + +.. option:: -3 + + This option uses an NSEC3-capable algorithm to generate a DNSSEC key. If this + option is used with an algorithm that has both NSEC and NSEC3 + versions, then the NSEC3 version is selected; for example, + ``dnssec-keygen -3 -a RSASHA1`` specifies the NSEC3RSASHA1 algorithm. + +.. option:: -a algorithm + + This option selects the cryptographic algorithm. For DNSSEC keys, the value of + ``algorithm`` must be one of RSASHA1, NSEC3RSASHA1, RSASHA256, + RSASHA512, ECDSAP256SHA256, ECDSAP384SHA384, ED25519, or ED448. For + TKEY, the value must be DH (Diffie-Hellman); specifying this value + automatically sets the :option:`-T KEY <-T>` option as well. + + These values are case-insensitive. In some cases, abbreviations are + supported, such as ECDSA256 for ECDSAP256SHA256 and ECDSA384 for + ECDSAP384SHA384. If RSASHA1 is specified along with the :option:`-3` + option, NSEC3RSASHA1 is used instead. + + This parameter *must* be specified except when using the :option:`-S` + option, which copies the algorithm from the predecessor key. + + In prior releases, HMAC algorithms could be generated for use as TSIG + keys, but that feature was removed in BIND 9.13.0. Use + :iscman:`tsig-keygen` to generate TSIG keys. + +.. option:: -b keysize + + This option specifies the number of bits in the key. The choice of key size + depends on the algorithm used: RSA keys must be between 1024 and 4096 + bits; Diffie-Hellman keys must be between 128 and 4096 bits. Elliptic + curve algorithms do not need this parameter. + + If the key size is not specified, some algorithms have pre-defined + defaults. For example, RSA keys for use as DNSSEC zone-signing keys + have a default size of 1024 bits; RSA keys for use as key-signing + keys (KSKs, generated with :option:`-f KSK <-f>`) default to 2048 bits. + +.. option:: -C + + This option enables compatibility mode, which generates an old-style key, without any timing + metadata. By default, :program:`dnssec-keygen` includes the key's + creation date in the metadata stored with the private key; other + dates may be set there as well, including publication date, activation date, + etc. Keys that include this data may be incompatible with older + versions of BIND; the :option:`-C` option suppresses them. + +.. option:: -c class + + This option indicates that the DNS record containing the key should have the + specified class. If not specified, class IN is used. + +.. option:: -d bits + + This option specifies the key size in bits. For the algorithms RSASHA1, NSEC3RSASA1, RSASHA256, and + RSASHA512 the key size must be between 1024 and 4096 bits; DH size is between 128 + and 4096 bits. This option is ignored for algorithms ECDSAP256SHA256, + ECDSAP384SHA384, ED25519, and ED448. + +.. option:: -E engine + + This option specifies the cryptographic hardware to use, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -f flag + + This option sets the specified flag in the flag field of the KEY/DNSKEY record. + The only recognized flags are KSK (Key-Signing Key) and REVOKE. + +.. option:: -G + + This option generates a key, but does not publish it or sign with it. This option is + incompatible with :option:`-P` and :option:`-A`. + +.. option:: -g generator + + This option indicates the generator to use if generating a Diffie-Hellman key. Allowed + values are 2 and 5. If no generator is specified, a known prime from + :rfc:`2539` is used if possible; otherwise the default is 2. + +.. option:: -h + + This option prints a short summary of the options and arguments to + :program:`dnssec-keygen`. + +.. option:: -K directory + + This option sets the directory in which the key files are to be written. + +.. option:: -k policy + + This option creates keys for a specific ``dnssec-policy``. If a policy uses multiple keys, + :program:`dnssec-keygen` generates multiple keys. This also + creates a ".state" file to keep track of the key state. + + This option creates keys according to the ``dnssec-policy`` configuration, hence + it cannot be used at the same time as many of the other options that + :program:`dnssec-keygen` provides. + +.. option:: -L ttl + + This option sets the default TTL to use for this key when it is converted into a + DNSKEY RR. This is the TTL used when the key is imported into a zone, + unless there was already a DNSKEY RRset in + place, in which case the existing TTL takes precedence. If this + value is not set and there is no existing DNSKEY RRset, the TTL + defaults to the SOA TTL. Setting the default TTL to ``0`` or ``none`` + is the same as leaving it unset. + +.. option:: -l file + + This option provides a configuration file that contains a ``dnssec-policy`` statement + (matching the policy set with :option:`-k`). + +.. option:: -n nametype + + This option specifies the owner type of the key. The value of ``nametype`` must + either be ZONE (for a DNSSEC zone key (KEY/DNSKEY)), HOST or ENTITY + (for a key associated with a host (KEY)), USER (for a key associated + with a user (KEY)), or OTHER (DNSKEY). These values are + case-insensitive. The default is ZONE for DNSKEY generation. + +.. option:: -p protocol + + This option sets the protocol value for the generated key, for use with + :option:`-T KEY <-T>`. The protocol is a number between 0 and 255. The default + is 3 (DNSSEC). Other possible values for this argument are listed in + :rfc:`2535` and its successors. + +.. option:: -q + + This option sets quiet mode, which suppresses unnecessary output, including progress + indication. Without this option, when :program:`dnssec-keygen` is run + interactively to generate an RSA or DSA key pair, it prints a + string of symbols to ``stderr`` indicating the progress of the key + generation. A ``.`` indicates that a random number has been found which + passed an initial sieve test; ``+`` means a number has passed a single + round of the Miller-Rabin primality test; and a space ( ) means that the + number has passed all the tests and is a satisfactory key. + +.. option:: -S key + + This option creates a new key which is an explicit successor to an existing key. + The name, algorithm, size, and type of the key are set to match + the existing key. The activation date of the new key is set to + the inactivation date of the existing one. The publication date is + set to the activation date minus the prepublication interval, + which defaults to 30 days. + +.. option:: -s strength + + This option specifies the strength value of the key. The strength is a number + between 0 and 15, and currently has no defined purpose in DNSSEC. + +.. option:: -T rrtype + + This option specifies the resource record type to use for the key. ``rrtype`` + must be either DNSKEY or KEY. The default is DNSKEY when using a + DNSSEC algorithm, but it can be overridden to KEY for use with + SIG(0). + +.. option:: -t type + + This option indicates the type of the key for use with :option:`-T KEY <-T>`. ``type`` + must be one of AUTHCONF, NOAUTHCONF, NOAUTH, or NOCONF. The default + is AUTHCONF. AUTH refers to the ability to authenticate data, and + CONF to the ability to encrypt data. + +.. option:: -V + + This option prints version information. + +.. option:: -v level + + This option sets the debugging level. + +Timing Options +~~~~~~~~~~~~~~ + +Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS +(which is the format used inside key files), +or 'Day Mon DD HH:MM:SS YYYY' (as printed by ``dnssec-settime -p``), +or UNIX epoch time (as printed by ``dnssec-settime -up``), +or the literal ``now``. + +The argument can be followed by ``+`` or ``-`` and an offset from the +given time. The literal ``now`` can be omitted before an offset. The +offset can be followed by one of the suffixes ``y``, ``mo``, ``w``, +``d``, ``h``, or ``mi``, so that it is computed in years (defined as +365 24-hour days, ignoring leap years), months (defined as 30 24-hour +days), weeks, days, hours, or minutes, respectively. Without a suffix, +the offset is computed in seconds. + +To unset a date, use ``none``, ``never``, or ``unset``. + +.. option:: -P date/offset + + This option sets the date on which a key is to be published to the zone. After + that date, the key is included in the zone but is not used + to sign it. If not set, and if the :option:`-G` option has not been used, the + default is the current date. + + .. program:: dnssec-keygen -P + .. option:: sync date/offset + + This option sets the date on which CDS and CDNSKEY records that match this key + are to be published to the zone. + +.. program:: dnssec-keygen + +.. option:: -A date/offset + + This option sets the date on which the key is to be activated. After that date, + the key is included in the zone and used to sign it. If not set, + and if the :option:`-G` option has not been used, the default is the current date. If set, + and :option:`-P` is not set, the publication date is set to the + activation date minus the prepublication interval. + +.. option:: -R date/offset + + This option sets the date on which the key is to be revoked. After that date, the + key is flagged as revoked. It is included in the zone and + is used to sign it. + +.. option:: -I date/offset + + This option sets the date on which the key is to be retired. After that date, the + key is still included in the zone, but it is not used to + sign it. + + +.. option:: -D date/offset + + This option sets the date on which the key is to be deleted. After that date, the + key is no longer included in the zone. (However, it may remain in the key + repository.) + + .. program:: dnssec-keygen -D + .. option:: sync date/offset + + This option sets the date on which the CDS and CDNSKEY records that match this + key are to be deleted. + +.. program:: dnssec-keygen + +.. option:: -i interval + + This option sets the prepublication interval for a key. If set, then the + publication and activation dates must be separated by at least this + much time. If the activation date is specified but the publication + date is not, the publication date defaults to this much time + before the activation date; conversely, if the publication date is + specified but not the activation date, activation is set to + this much time after publication. + + If the key is being created as an explicit successor to another key, + then the default prepublication interval is 30 days; otherwise it is + zero. + + As with date offsets, if the argument is followed by one of the + suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is + measured in years, months, weeks, days, hours, or minutes, + respectively. Without a suffix, the interval is measured in seconds. + +Generated Keys +~~~~~~~~~~~~~~ + +When :program:`dnssec-keygen` completes successfully, it prints a string of the +form ``Knnnn.+aaa+iiiii`` to the standard output. This is an +identification string for the key it has generated. + +- ``nnnn`` is the key name. + +- ``aaa`` is the numeric representation of the algorithm. + +- ``iiiii`` is the key identifier (or footprint). + +:program:`dnssec-keygen` creates two files, with names based on the printed +string. ``Knnnn.+aaa+iiiii.key`` contains the public key, and +``Knnnn.+aaa+iiiii.private`` contains the private key. + +The ``.key`` file contains a DNSKEY or KEY record. When a zone is being +signed by :iscman:`named` or :option:`dnssec-signzone -S`, DNSKEY records are +included automatically. In other cases, the ``.key`` file can be +inserted into a zone file manually or with an ``$INCLUDE`` statement. + +The ``.private`` file contains algorithm-specific fields. For obvious +security reasons, this file does not have general read permission. + +Example +~~~~~~~ + +To generate an ECDSAP256SHA256 zone-signing key for the zone +``example.com``, issue the command: + +``dnssec-keygen -a ECDSAP256SHA256 example.com`` + +The command prints a string of the form: + +``Kexample.com.+013+26160`` + +In this example, :program:`dnssec-keygen` creates the files +``Kexample.com.+013+26160.key`` and ``Kexample.com.+013+26160.private``. + +To generate a matching key-signing key, issue the command: + +``dnssec-keygen -a ECDSAP256SHA256 -f KSK example.com`` + +See Also +~~~~~~~~ + +:iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, :rfc:`2539`, +:rfc:`2845`, :rfc:`4034`. diff --git a/bin/dnssec/dnssec-revoke.c b/bin/dnssec/dnssec-revoke.c new file mode 100644 index 0000000..93ee20c --- /dev/null +++ b/bin/dnssec/dnssec-revoke.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-revoke"; + +static isc_mem_t *mctx = NULL; + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options] keyfile\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, " -E engine: specify OpenSSL engine\n"); + fprintf(stderr, " -f: force overwrite\n"); + fprintf(stderr, " -h: help\n"); + fprintf(stderr, " -K directory: use directory for key files\n"); + fprintf(stderr, " -r: remove old keyfiles after " + "creating revoked version\n"); + fprintf(stderr, " -v level: set level of verbosity\n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, "Output:\n"); + fprintf(stderr, " K++.key, " + "K++.private\n"); + + exit(-1); +} + +int +main(int argc, char **argv) { + isc_result_t result; + const char *engine = NULL; + char const *filename = NULL; + char *dir = NULL; + char newname[1024], oldname[1024]; + char keystr[DST_KEY_FORMATSIZE]; + char *endp; + int ch; + dst_key_t *key = NULL; + uint32_t flags; + isc_buffer_t buf; + bool force = false; + bool removefile = false; + bool id = false; + + if (argc == 1) { + usage(); + } + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + + while ((ch = isc_commandline_parse(argc, argv, "E:fK:rRhv:V")) != -1) { + switch (ch) { + case 'E': + engine = isc_commandline_argument; + break; + case 'f': + force = true; + break; + case 'K': + /* + * We don't have to copy it here, but do it to + * simplify cleanup later + */ + dir = isc_mem_strdup(mctx, isc_commandline_argument); + break; + case 'r': + removefile = true; + break; + case 'R': + id = true; + break; + case 'v': + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + if (argc < isc_commandline_index + 1 || + argv[isc_commandline_index] == NULL) + { + fatal("The key file name was not specified"); + } + if (argc > isc_commandline_index + 1) { + fatal("Extraneous arguments"); + } + + if (dir != NULL) { + filename = argv[isc_commandline_index]; + } else { + result = isc_file_splitpath(mctx, argv[isc_commandline_index], + &dir, &filename); + if (result != ISC_R_SUCCESS) { + fatal("cannot process filename %s: %s", + argv[isc_commandline_index], + isc_result_totext(result)); + } + if (strcmp(dir, ".") == 0) { + isc_mem_free(mctx, dir); + dir = NULL; + } + } + + result = dst_lib_init(mctx, engine); + if (result != ISC_R_SUCCESS) { + fatal("Could not initialize dst: %s", + isc_result_totext(result)); + } + + result = dst_key_fromnamedfile( + filename, dir, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &key); + if (result != ISC_R_SUCCESS) { + fatal("Invalid keyfile name %s: %s", filename, + isc_result_totext(result)); + } + + if (id) { + fprintf(stdout, "%u\n", dst_key_rid(key)); + goto cleanup; + } + dst_key_format(key, keystr, sizeof(keystr)); + + if (verbose > 2) { + fprintf(stderr, "%s: %s\n", program, keystr); + } + + if (force) { + set_keyversion(key); + } else { + check_keyversion(key, keystr); + } + + flags = dst_key_flags(key); + if ((flags & DNS_KEYFLAG_REVOKE) == 0) { + isc_stdtime_t now; + + if ((flags & DNS_KEYFLAG_KSK) == 0) { + fprintf(stderr, + "%s: warning: Key is not flagged " + "as a KSK. Revoking a ZSK is " + "legal, but undefined.\n", + program); + } + + isc_stdtime_get(&now); + dst_key_settime(key, DST_TIME_REVOKE, now); + + dst_key_setflags(key, flags | DNS_KEYFLAG_REVOKE); + + isc_buffer_init(&buf, newname, sizeof(newname)); + dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); + + if (access(newname, F_OK) == 0 && !force) { + fatal("Key file %s already exists; " + "use -f to force overwrite", + newname); + } + + result = dst_key_tofile(key, DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, + dir); + if (result != ISC_R_SUCCESS) { + dst_key_format(key, keystr, sizeof(keystr)); + fatal("Failed to write key %s: %s", keystr, + isc_result_totext(result)); + } + + isc_buffer_clear(&buf); + dst_key_buildfilename(key, 0, dir, &buf); + printf("%s\n", newname); + + /* + * Remove old key file, if told to (and if + * it isn't the same as the new file) + */ + if (removefile) { + isc_buffer_init(&buf, oldname, sizeof(oldname)); + dst_key_setflags(key, flags & ~DNS_KEYFLAG_REVOKE); + dst_key_buildfilename(key, DST_TYPE_PRIVATE, dir, &buf); + if (strcmp(oldname, newname) == 0) { + goto cleanup; + } + (void)unlink(oldname); + isc_buffer_clear(&buf); + dst_key_buildfilename(key, DST_TYPE_PUBLIC, dir, &buf); + (void)unlink(oldname); + } + } else { + dst_key_format(key, keystr, sizeof(keystr)); + fatal("Key %s is already revoked", keystr); + } + +cleanup: + dst_key_free(&key); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + if (dir != NULL) { + isc_mem_free(mctx, dir); + } + isc_mem_destroy(&mctx); + + return (0); +} diff --git a/bin/dnssec/dnssec-revoke.rst b/bin/dnssec/dnssec-revoke.rst new file mode 100644 index 0000000..052865f --- /dev/null +++ b/bin/dnssec/dnssec-revoke.rst @@ -0,0 +1,78 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-revoke +.. program:: dnssec-revoke +.. _man_dnssec-revoke: + +dnssec-revoke - set the REVOKED bit on a DNSSEC key +--------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-revoke` [**-hr**] [**-v** level] [**-V**] [**-K** directory] [**-E** engine] [**-f**] [**-R**] {keyfile} + +Description +~~~~~~~~~~~ + +:program:`dnssec-revoke` reads a DNSSEC key file, sets the REVOKED bit on the +key as defined in :rfc:`5011`, and creates a new pair of key files +containing the now-revoked key. + +Options +~~~~~~~ + +.. option:: -h + + This option emits a usage message and exits. + +.. option:: -K directory + + This option sets the directory in which the key files are to reside. + +.. option:: -r + + This option indicates to remove the original keyset files after writing the new keyset files. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -V + + This option prints version information. + +.. option:: -E engine + + This option specifies the cryptographic hardware to use, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -f + + This option indicates a forced overwrite and causes :program:`dnssec-revoke` to write the new key pair, + even if a file already exists matching the algorithm and key ID of + the revoked key. + +.. option:: -R + + This option prints the key tag of the key with the REVOKE bit set, but does not + revoke the key. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, BIND 9 Administrator Reference Manual, :rfc:`5011`. diff --git a/bin/dnssec/dnssec-settime.c b/bin/dnssec/dnssec-settime.c new file mode 100644 index 0000000..100ff88 --- /dev/null +++ b/bin/dnssec/dnssec-settime.c @@ -0,0 +1,967 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-settime"; + +static isc_mem_t *mctx = NULL; + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options] keyfile\n\n", program); + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + fprintf(stderr, "General options:\n"); + fprintf(stderr, " -E engine: specify OpenSSL engine\n"); + fprintf(stderr, " -f: force update of old-style " + "keys\n"); + fprintf(stderr, " -K directory: set key file location\n"); + fprintf(stderr, " -L ttl: set default key TTL\n"); + fprintf(stderr, " -v level: set level of verbosity\n"); + fprintf(stderr, " -V: print version information\n"); + fprintf(stderr, " -h: help\n"); + fprintf(stderr, "Timing options:\n"); + fprintf(stderr, " -P date/[+-]offset/none: set/unset key " + "publication date\n"); + fprintf(stderr, " -P ds date/[+-]offset/none: set/unset " + "DS publication date\n"); + fprintf(stderr, " -P sync date/[+-]offset/none: set/unset " + "CDS and CDNSKEY publication date\n"); + fprintf(stderr, " -A date/[+-]offset/none: set/unset key " + "activation date\n"); + fprintf(stderr, " -R date/[+-]offset/none: set/unset key " + "revocation date\n"); + fprintf(stderr, " -I date/[+-]offset/none: set/unset key " + "inactivation date\n"); + fprintf(stderr, " -D date/[+-]offset/none: set/unset key " + "deletion date\n"); + fprintf(stderr, " -D ds date/[+-]offset/none: set/unset " + "DS deletion date\n"); + fprintf(stderr, " -D sync date/[+-]offset/none: set/unset " + "CDS and CDNSKEY deletion date\n"); + fprintf(stderr, " -S : generate a successor to an existing " + "key\n"); + fprintf(stderr, " -i : prepublication interval for " + "successor key " + "(default: 30 days)\n"); + fprintf(stderr, "Key state options:\n"); + fprintf(stderr, " -s: update key state file (default no)\n"); + fprintf(stderr, " -g state: set the goal state for this key\n"); + fprintf(stderr, " -d state date/[+-]offset: set the DS state\n"); + fprintf(stderr, " -k state date/[+-]offset: set the DNSKEY state\n"); + fprintf(stderr, " -r state date/[+-]offset: set the RRSIG (KSK) " + "state\n"); + fprintf(stderr, " -z state date/[+-]offset: set the RRSIG (ZSK) " + "state\n"); + fprintf(stderr, "Printing options:\n"); + fprintf(stderr, " -p C/P/Psync/A/R/I/D/Dsync/all: print a " + "particular time value or values\n"); + fprintf(stderr, " -u: print times in unix epoch " + "format\n"); + fprintf(stderr, "Output:\n"); + fprintf(stderr, " K++.key, " + "K++.private\n"); + + exit(-1); +} + +static void +printtime(dst_key_t *key, int type, const char *tag, bool epoch, FILE *stream) { + isc_result_t result; + isc_stdtime_t when; + + if (tag != NULL) { + fprintf(stream, "%s: ", tag); + } + + result = dst_key_gettime(key, type, &when); + if (result == ISC_R_NOTFOUND) { + fprintf(stream, "UNSET\n"); + } else if (epoch) { + fprintf(stream, "%d\n", (int)when); + } else { + time_t now = when; + struct tm t, *tm = localtime_r(&now, &t); + unsigned int flen; + char timebuf[80]; + + if (tm == NULL) { + fprintf(stream, "INVALID\n"); + return; + } + + flen = strftime(timebuf, sizeof(timebuf), + "%a %b %e %H:%M:%S %Y", tm); + INSIST(flen > 0U && flen < sizeof(timebuf)); + fprintf(stream, "%s\n", timebuf); + } +} + +static void +writekey(dst_key_t *key, const char *directory, bool write_state) { + char newname[1024]; + char keystr[DST_KEY_FORMATSIZE]; + isc_buffer_t buf; + isc_result_t result; + int options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE; + + if (write_state) { + options |= DST_TYPE_STATE; + } + + isc_buffer_init(&buf, newname, sizeof(newname)); + result = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build public key filename: %s", + isc_result_totext(result)); + } + + result = dst_key_tofile(key, options, directory); + if (result != ISC_R_SUCCESS) { + dst_key_format(key, keystr, sizeof(keystr)); + fatal("Failed to write key %s: %s", keystr, + isc_result_totext(result)); + } + printf("%s\n", newname); + + isc_buffer_clear(&buf); + result = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build private key filename: %s", + isc_result_totext(result)); + } + printf("%s\n", newname); + + if (write_state) { + isc_buffer_clear(&buf); + result = dst_key_buildfilename(key, DST_TYPE_STATE, directory, + &buf); + if (result != ISC_R_SUCCESS) { + fatal("Failed to build key state filename: %s", + isc_result_totext(result)); + } + printf("%s\n", newname); + } +} + +int +main(int argc, char **argv) { + isc_result_t result; + const char *engine = NULL; + const char *filename = NULL; + char *directory = NULL; + char keystr[DST_KEY_FORMATSIZE]; + char *endp, *p; + int ch; + const char *predecessor = NULL; + dst_key_t *prevkey = NULL; + dst_key_t *key = NULL; + dns_name_t *name = NULL; + dns_secalg_t alg = 0; + unsigned int size = 0; + uint16_t flags = 0; + int prepub = -1; + int options; + dns_ttl_t ttl = 0; + isc_stdtime_t now; + isc_stdtime_t dstime = 0, dnskeytime = 0; + isc_stdtime_t krrsigtime = 0, zrrsigtime = 0; + isc_stdtime_t pub = 0, act = 0, rev = 0, inact = 0, del = 0; + isc_stdtime_t prevact = 0, previnact = 0, prevdel = 0; + dst_key_state_t goal = DST_KEY_STATE_NA; + dst_key_state_t ds = DST_KEY_STATE_NA; + dst_key_state_t dnskey = DST_KEY_STATE_NA; + dst_key_state_t krrsig = DST_KEY_STATE_NA; + dst_key_state_t zrrsig = DST_KEY_STATE_NA; + bool setgoal = false, setds = false, setdnskey = false; + bool setkrrsig = false, setzrrsig = false; + bool setdstime = false, setdnskeytime = false; + bool setkrrsigtime = false, setzrrsigtime = false; + bool setpub = false, setact = false; + bool setrev = false, setinact = false; + bool setdel = false, setttl = false; + bool unsetpub = false, unsetact = false; + bool unsetrev = false, unsetinact = false; + bool unsetdel = false; + bool printcreate = false, printpub = false; + bool printact = false, printrev = false; + bool printinact = false, printdel = false; + bool force = false; + bool epoch = false; + bool changed = false; + bool write_state = false; + isc_log_t *log = NULL; + isc_stdtime_t syncadd = 0, syncdel = 0; + bool unsetsyncadd = false, setsyncadd = false; + bool unsetsyncdel = false, setsyncdel = false; + bool printsyncadd = false, printsyncdel = false; + isc_stdtime_t dsadd = 0, dsdel = 0; + bool unsetdsadd = false, setdsadd = false; + bool unsetdsdel = false, setdsdel = false; + bool printdsadd = false, printdsdel = false; + + options = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE; + + if (argc == 1) { + usage(); + } + + isc_mem_create(&mctx); + + setup_logging(mctx, &log); + + isc_commandline_errprint = false; + + isc_stdtime_get(&now); + +#define CMDLINE_FLAGS "A:D:d:E:fg:hI:i:K:k:L:P:p:R:r:S:suv:Vz:" + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'A': + if (setact || unsetact) { + fatal("-A specified more than once"); + } + + changed = true; + act = strtotime(isc_commandline_argument, now, now, + &setact); + unsetact = !setact; + break; + case 'D': + /* -Dsync ? */ + if (isoptarg("sync", argv, usage)) { + if (unsetsyncdel || setsyncdel) { + fatal("-D sync specified more than " + "once"); + } + + changed = true; + syncdel = strtotime(isc_commandline_argument, + now, now, &setsyncdel); + unsetsyncdel = !setsyncdel; + break; + } + /* -Dds ? */ + if (isoptarg("ds", argv, usage)) { + if (unsetdsdel || setdsdel) { + fatal("-D ds specified more than once"); + } + + changed = true; + dsdel = strtotime(isc_commandline_argument, now, + now, &setdsdel); + unsetdsdel = !setdsdel; + break; + } + /* -Ddnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setdel || unsetdel) { + fatal("-D specified more than once"); + } + + changed = true; + del = strtotime(isc_commandline_argument, now, now, + &setdel); + unsetdel = !setdel; + break; + case 'd': + if (setds) { + fatal("-d specified more than once"); + } + + ds = strtokeystate(isc_commandline_argument); + setds = true; + /* time */ + (void)isoptarg(isc_commandline_argument, argv, usage); + dstime = strtotime(isc_commandline_argument, now, now, + &setdstime); + break; + case 'E': + engine = isc_commandline_argument; + break; + case 'f': + force = true; + break; + case 'g': + if (setgoal) { + fatal("-g specified more than once"); + } + + goal = strtokeystate(isc_commandline_argument); + if (goal != DST_KEY_STATE_NA && + goal != DST_KEY_STATE_HIDDEN && + goal != DST_KEY_STATE_OMNIPRESENT) + { + fatal("-g must be either none, hidden, or " + "omnipresent"); + } + setgoal = true; + break; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + case 'I': + if (setinact || unsetinact) { + fatal("-I specified more than once"); + } + + changed = true; + inact = strtotime(isc_commandline_argument, now, now, + &setinact); + unsetinact = !setinact; + break; + case 'i': + prepub = strtottl(isc_commandline_argument); + break; + case 'K': + /* + * We don't have to copy it here, but do it to + * simplify cleanup later + */ + directory = isc_mem_strdup(mctx, + isc_commandline_argument); + break; + case 'k': + if (setdnskey) { + fatal("-k specified more than once"); + } + + dnskey = strtokeystate(isc_commandline_argument); + setdnskey = true; + /* time */ + (void)isoptarg(isc_commandline_argument, argv, usage); + dnskeytime = strtotime(isc_commandline_argument, now, + now, &setdnskeytime); + break; + case 'L': + ttl = strtottl(isc_commandline_argument); + setttl = true; + break; + case 'P': + /* -Psync ? */ + if (isoptarg("sync", argv, usage)) { + if (unsetsyncadd || setsyncadd) { + fatal("-P sync specified more than " + "once"); + } + + changed = true; + syncadd = strtotime(isc_commandline_argument, + now, now, &setsyncadd); + unsetsyncadd = !setsyncadd; + break; + } + /* -Pds ? */ + if (isoptarg("ds", argv, usage)) { + if (unsetdsadd || setdsadd) { + fatal("-P ds specified more than once"); + } + + changed = true; + dsadd = strtotime(isc_commandline_argument, now, + now, &setdsadd); + unsetdsadd = !setdsadd; + break; + } + /* -Pdnskey ? */ + (void)isoptarg("dnskey", argv, usage); + if (setpub || unsetpub) { + fatal("-P specified more than once"); + } + + changed = true; + pub = strtotime(isc_commandline_argument, now, now, + &setpub); + unsetpub = !setpub; + break; + case 'p': + p = isc_commandline_argument; + if (!strcasecmp(p, "all")) { + printcreate = true; + printpub = true; + printact = true; + printrev = true; + printinact = true; + printdel = true; + printsyncadd = true; + printsyncdel = true; + printdsadd = true; + printdsdel = true; + break; + } + + do { + switch (*p++) { + case 'A': + printact = true; + break; + case 'C': + printcreate = true; + break; + case 'D': + if (!strncmp(p, "sync", 4)) { + p += 4; + printsyncdel = true; + break; + } + if (!strncmp(p, "ds", 2)) { + p += 2; + printdsdel = true; + break; + } + printdel = true; + break; + case 'I': + printinact = true; + break; + case 'P': + if (!strncmp(p, "sync", 4)) { + p += 4; + printsyncadd = true; + break; + } + if (!strncmp(p, "ds", 2)) { + p += 2; + printdsadd = true; + break; + } + printpub = true; + break; + case 'R': + printrev = true; + break; + case ' ': + break; + default: + usage(); + break; + } + } while (*p != '\0'); + break; + case 'R': + if (setrev || unsetrev) { + fatal("-R specified more than once"); + } + + changed = true; + rev = strtotime(isc_commandline_argument, now, now, + &setrev); + unsetrev = !setrev; + break; + case 'r': + if (setkrrsig) { + fatal("-r specified more than once"); + } + + krrsig = strtokeystate(isc_commandline_argument); + setkrrsig = true; + /* time */ + (void)isoptarg(isc_commandline_argument, argv, usage); + krrsigtime = strtotime(isc_commandline_argument, now, + now, &setkrrsigtime); + break; + case 'S': + predecessor = isc_commandline_argument; + break; + case 's': + write_state = true; + break; + case 'u': + epoch = true; + break; + case 'V': + /* Does not return. */ + version(program); + case 'v': + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("-v must be followed by a number"); + } + break; + case 'z': + if (setzrrsig) { + fatal("-z specified more than once"); + } + + zrrsig = strtokeystate(isc_commandline_argument); + setzrrsig = true; + (void)isoptarg(isc_commandline_argument, argv, usage); + zrrsigtime = strtotime(isc_commandline_argument, now, + now, &setzrrsigtime); + break; + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + if (argc < isc_commandline_index + 1 || + argv[isc_commandline_index] == NULL) + { + fatal("The key file name was not specified"); + } + if (argc > isc_commandline_index + 1) { + fatal("Extraneous arguments"); + } + + if ((setgoal || setds || setdnskey || setkrrsig || setzrrsig) && + !write_state) + { + fatal("Options -g, -d, -k, -r and -z require -s to be set"); + } + + result = dst_lib_init(mctx, engine); + if (result != ISC_R_SUCCESS) { + fatal("Could not initialize dst: %s", + isc_result_totext(result)); + } + + if (predecessor != NULL) { + int major, minor; + + if (prepub == -1) { + prepub = (30 * 86400); + } + + if (setpub || unsetpub) { + fatal("-S and -P cannot be used together"); + } + if (setact || unsetact) { + fatal("-S and -A cannot be used together"); + } + + result = dst_key_fromnamedfile(predecessor, directory, options, + mctx, &prevkey); + if (result != ISC_R_SUCCESS) { + fatal("Invalid keyfile %s: %s", filename, + isc_result_totext(result)); + } + if (!dst_key_isprivate(prevkey) && !dst_key_isexternal(prevkey)) + { + fatal("%s is not a private key", filename); + } + + name = dst_key_name(prevkey); + alg = dst_key_alg(prevkey); + size = dst_key_size(prevkey); + flags = dst_key_flags(prevkey); + + dst_key_format(prevkey, keystr, sizeof(keystr)); + dst_key_getprivateformat(prevkey, &major, &minor); + if (major != DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { + fatal("Predecessor has incompatible format " + "version %d.%d\n\t", + major, minor); + } + + result = dst_key_gettime(prevkey, DST_TIME_ACTIVATE, &prevact); + if (result != ISC_R_SUCCESS) { + fatal("Predecessor has no activation date. " + "You must set one before\n\t" + "generating a successor."); + } + + result = dst_key_gettime(prevkey, DST_TIME_INACTIVE, + &previnact); + if (result != ISC_R_SUCCESS) { + fatal("Predecessor has no inactivation date. " + "You must set one before\n\t" + "generating a successor."); + } + + pub = previnact - prepub; + act = previnact; + + if ((previnact - prepub) < now && prepub != 0) { + fatal("Time until predecessor inactivation is\n\t" + "shorter than the prepublication interval. " + "Either change\n\t" + "predecessor inactivation date, or use the -i " + "option to set\n\t" + "a shorter prepublication interval."); + } + + result = dst_key_gettime(prevkey, DST_TIME_DELETE, &prevdel); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "%s: warning: Predecessor has no " + "removal date;\n\t" + "it will remain in the zone " + "indefinitely after rollover.\n", + program); + } else if (prevdel < previnact) { + fprintf(stderr, + "%s: warning: Predecessor is " + "scheduled to be deleted\n\t" + "before it is scheduled to be " + "inactive.\n", + program); + } + + changed = setpub = setact = true; + } else { + if (prepub < 0) { + prepub = 0; + } + + if (prepub > 0) { + if (setpub && setact && (act - prepub) < pub) { + fatal("Activation and publication dates " + "are closer together than the\n\t" + "prepublication interval."); + } + + if (setpub && !setact) { + setact = true; + act = pub + prepub; + } else if (setact && !setpub) { + setpub = true; + pub = act - prepub; + } + + if ((act - prepub) < now) { + fatal("Time until activation is shorter " + "than the\n\tprepublication interval."); + } + } + } + + if (directory != NULL) { + filename = argv[isc_commandline_index]; + } else { + result = isc_file_splitpath(mctx, argv[isc_commandline_index], + &directory, &filename); + if (result != ISC_R_SUCCESS) { + fatal("cannot process filename %s: %s", + argv[isc_commandline_index], + isc_result_totext(result)); + } + } + + result = dst_key_fromnamedfile(filename, directory, options, mctx, + &key); + if (result != ISC_R_SUCCESS) { + fatal("Invalid keyfile %s: %s", filename, + isc_result_totext(result)); + } + + if (!dst_key_isprivate(key) && !dst_key_isexternal(key)) { + fatal("%s is not a private key", filename); + } + + dst_key_format(key, keystr, sizeof(keystr)); + + if (predecessor != NULL) { + if (!dns_name_equal(name, dst_key_name(key))) { + fatal("Key name mismatch"); + } + if (alg != dst_key_alg(key)) { + fatal("Key algorithm mismatch"); + } + if (size != dst_key_size(key)) { + fatal("Key size mismatch"); + } + if (flags != dst_key_flags(key)) { + fatal("Key flags mismatch"); + } + } + + prevdel = previnact = 0; + if ((setdel && setinact && del < inact) || + (dst_key_gettime(key, DST_TIME_INACTIVE, &previnact) == + ISC_R_SUCCESS && + setdel && !setinact && !unsetinact && del < previnact) || + (dst_key_gettime(key, DST_TIME_DELETE, &prevdel) == ISC_R_SUCCESS && + setinact && !setdel && !unsetdel && prevdel < inact) || + (!setdel && !unsetdel && !setinact && !unsetinact && prevdel != 0 && + prevdel < previnact)) + { + fprintf(stderr, + "%s: warning: Key is scheduled to " + "be deleted before it is\n\t" + "scheduled to be inactive.\n", + program); + } + + if (force) { + set_keyversion(key); + } else { + check_keyversion(key, keystr); + } + + if (verbose > 2) { + fprintf(stderr, "%s: %s\n", program, keystr); + } + + /* + * Set time values. + */ + if (setpub) { + dst_key_settime(key, DST_TIME_PUBLISH, pub); + } else if (unsetpub) { + dst_key_unsettime(key, DST_TIME_PUBLISH); + } + + if (setact) { + dst_key_settime(key, DST_TIME_ACTIVATE, act); + } else if (unsetact) { + dst_key_unsettime(key, DST_TIME_ACTIVATE); + } + + if (setrev) { + if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) { + fprintf(stderr, + "%s: warning: Key %s is already " + "revoked; changing the revocation date " + "will not affect this.\n", + program, keystr); + } + if ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0) { + fprintf(stderr, + "%s: warning: Key %s is not flagged as " + "a KSK, but -R was used. Revoking a " + "ZSK is legal, but undefined.\n", + program, keystr); + } + dst_key_settime(key, DST_TIME_REVOKE, rev); + } else if (unsetrev) { + if ((dst_key_flags(key) & DNS_KEYFLAG_REVOKE) != 0) { + fprintf(stderr, + "%s: warning: Key %s is already " + "revoked; removing the revocation date " + "will not affect this.\n", + program, keystr); + } + dst_key_unsettime(key, DST_TIME_REVOKE); + } + + if (setinact) { + dst_key_settime(key, DST_TIME_INACTIVE, inact); + } else if (unsetinact) { + dst_key_unsettime(key, DST_TIME_INACTIVE); + } + + if (setdel) { + dst_key_settime(key, DST_TIME_DELETE, del); + } else if (unsetdel) { + dst_key_unsettime(key, DST_TIME_DELETE); + } + + if (setsyncadd) { + dst_key_settime(key, DST_TIME_SYNCPUBLISH, syncadd); + } else if (unsetsyncadd) { + dst_key_unsettime(key, DST_TIME_SYNCPUBLISH); + } + + if (setsyncdel) { + dst_key_settime(key, DST_TIME_SYNCDELETE, syncdel); + } else if (unsetsyncdel) { + dst_key_unsettime(key, DST_TIME_SYNCDELETE); + } + + if (setdsadd) { + dst_key_settime(key, DST_TIME_DSPUBLISH, dsadd); + } else if (unsetdsadd) { + dst_key_unsettime(key, DST_TIME_DSPUBLISH); + } + + if (setdsdel) { + dst_key_settime(key, DST_TIME_DSDELETE, dsdel); + } else if (unsetdsdel) { + dst_key_unsettime(key, DST_TIME_DSDELETE); + } + + if (setttl) { + dst_key_setttl(key, ttl); + } + + if (predecessor != NULL && prevkey != NULL) { + dst_key_setnum(prevkey, DST_NUM_SUCCESSOR, dst_key_id(key)); + dst_key_setnum(key, DST_NUM_PREDECESSOR, dst_key_id(prevkey)); + } + + /* + * No metadata changes were made but we're forcing an upgrade + * to the new format anyway: use "-P now -A now" as the default + */ + if (force && !changed) { + dst_key_settime(key, DST_TIME_PUBLISH, now); + dst_key_settime(key, DST_TIME_ACTIVATE, now); + changed = true; + } + + /* + * Make sure the key state goals are written. + */ + if (write_state) { + if (setgoal) { + if (goal == DST_KEY_STATE_NA) { + dst_key_unsetstate(key, DST_KEY_GOAL); + } else { + dst_key_setstate(key, DST_KEY_GOAL, goal); + } + changed = true; + } + if (setds) { + if (ds == DST_KEY_STATE_NA) { + dst_key_unsetstate(key, DST_KEY_DS); + dst_key_unsettime(key, DST_TIME_DS); + } else { + dst_key_setstate(key, DST_KEY_DS, ds); + dst_key_settime(key, DST_TIME_DS, dstime); + } + changed = true; + } + if (setdnskey) { + if (dnskey == DST_KEY_STATE_NA) { + dst_key_unsetstate(key, DST_KEY_DNSKEY); + dst_key_unsettime(key, DST_TIME_DNSKEY); + } else { + dst_key_setstate(key, DST_KEY_DNSKEY, dnskey); + dst_key_settime(key, DST_TIME_DNSKEY, + dnskeytime); + } + changed = true; + } + if (setkrrsig) { + if (krrsig == DST_KEY_STATE_NA) { + dst_key_unsetstate(key, DST_KEY_KRRSIG); + dst_key_unsettime(key, DST_TIME_KRRSIG); + } else { + dst_key_setstate(key, DST_KEY_KRRSIG, krrsig); + dst_key_settime(key, DST_TIME_KRRSIG, + krrsigtime); + } + changed = true; + } + if (setzrrsig) { + if (zrrsig == DST_KEY_STATE_NA) { + dst_key_unsetstate(key, DST_KEY_ZRRSIG); + dst_key_unsettime(key, DST_TIME_ZRRSIG); + } else { + dst_key_setstate(key, DST_KEY_ZRRSIG, zrrsig); + dst_key_settime(key, DST_TIME_ZRRSIG, + zrrsigtime); + } + changed = true; + } + } + + if (!changed && setttl) { + changed = true; + } + + /* + * Print out time values, if -p was used. + */ + if (printcreate) { + printtime(key, DST_TIME_CREATED, "Created", epoch, stdout); + } + + if (printpub) { + printtime(key, DST_TIME_PUBLISH, "Publish", epoch, stdout); + } + + if (printact) { + printtime(key, DST_TIME_ACTIVATE, "Activate", epoch, stdout); + } + + if (printrev) { + printtime(key, DST_TIME_REVOKE, "Revoke", epoch, stdout); + } + + if (printinact) { + printtime(key, DST_TIME_INACTIVE, "Inactive", epoch, stdout); + } + + if (printdel) { + printtime(key, DST_TIME_DELETE, "Delete", epoch, stdout); + } + + if (printsyncadd) { + printtime(key, DST_TIME_SYNCPUBLISH, "SYNC Publish", epoch, + stdout); + } + + if (printsyncdel) { + printtime(key, DST_TIME_SYNCDELETE, "SYNC Delete", epoch, + stdout); + } + + if (printdsadd) { + printtime(key, DST_TIME_DSPUBLISH, "DS Publish", epoch, stdout); + } + + if (printdsdel) { + printtime(key, DST_TIME_DSDELETE, "DS Delete", epoch, stdout); + } + + if (changed) { + writekey(key, directory, write_state); + if (predecessor != NULL && prevkey != NULL) { + writekey(prevkey, directory, write_state); + } + } + + if (prevkey != NULL) { + dst_key_free(&prevkey); + } + dst_key_free(&key); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + cleanup_logging(&log); + isc_mem_free(mctx, directory); + isc_mem_destroy(&mctx); + + return (0); +} diff --git a/bin/dnssec/dnssec-settime.rst b/bin/dnssec/dnssec-settime.rst new file mode 100644 index 0000000..5cb4ea8 --- /dev/null +++ b/bin/dnssec/dnssec-settime.rst @@ -0,0 +1,271 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-settime +.. program:: dnssec-settime +.. _man_dnssec-settime: + +dnssec-settime: set the key timing metadata for a DNSSEC key +------------------------------------------------------------ + +Synopsis +~~~~~~~~ + +:program:`dnssec-settime` [**-f**] [**-K** directory] [**-L** ttl] [**-P** date/offset] [**-P** ds date/offset] [**-P** sync date/offset] [**-A** date/offset] [**-R** date/offset] [**-I** date/offset] [**-D** date/offset] [**-D** ds date/offset] [**-D** sync date/offset] [**-S** key] [**-i** interval] [**-h**] [**-V**] [**-v** level] [**-E** engine] {keyfile} [**-s**] [**-g** state] [**-d** state date/offset] [**-k** state date/offset] [**-r** state date/offset] [**-z** state date/offset] + +Description +~~~~~~~~~~~ + +:program:`dnssec-settime` reads a DNSSEC private key file and sets the key +timing metadata as specified by the :option:`-P`, :option:`-A`, :option:`-R`, +:option:`-I`, and :option:`-D` options. The metadata can then be used by +:iscman:`dnssec-signzone` or other signing software to determine when a key is +to be published, whether it should be used for signing a zone, etc. + +If none of these options is set on the command line, +:program:`dnssec-settime` simply prints the key timing metadata already stored +in the key. + +When key metadata fields are changed, both files of a key pair +(``Knnnn.+aaa+iiiii.key`` and ``Knnnn.+aaa+iiiii.private``) are +regenerated. + +Metadata fields are stored in the private file. A +human-readable description of the metadata is also placed in comments in +the key file. The private file's permissions are always set to be +inaccessible to anyone other than the owner (mode 0600). + +When working with state files, it is possible to update the timing metadata in +those files as well with :option:`-s`. With this option, it is also possible +to update key states with :option:`-d` (DS), :option:`-k` (DNSKEY), :option:`-r` +(RRSIG of KSK), or :option:`-z` (RRSIG of ZSK). Allowed states are HIDDEN, +RUMOURED, OMNIPRESENT, and UNRETENTIVE. + +The goal state of the key can also be set with :option:`-g`. This should be either +HIDDEN or OMNIPRESENT, representing whether the key should be removed from the +zone or published. + +It is NOT RECOMMENDED to manipulate state files manually, except for testing +purposes. + +Options +~~~~~~~ + +.. option:: -f + + This option forces an update of an old-format key with no metadata fields. Without + this option, :program:`dnssec-settime` fails when attempting to update a + legacy key. With this option, the key is recreated in the new + format, but with the original key data retained. The key's creation + date is set to the present time. If no other values are + specified, then the key's publication and activation dates are also + set to the present time. + +.. option:: -K directory + + This option sets the directory in which the key files are to reside. + +.. option:: -L ttl + + This option sets the default TTL to use for this key when it is converted into a + DNSKEY RR. This is the TTL used when the key is imported into a zone, + unless there was already a DNSKEY RRset in + place, in which case the existing TTL takes precedence. If this + value is not set and there is no existing DNSKEY RRset, the TTL + defaults to the SOA TTL. Setting the default TTL to ``0`` or ``none`` + removes it from the key. + +.. option:: -h + + This option emits a usage message and exits. + +.. option:: -V + + This option prints version information. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -E engine + + This option specifies the cryptographic hardware to use, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +Timing Options +~~~~~~~~~~~~~~ + +Dates can be expressed in the format YYYYMMDD or YYYYMMDDHHMMSS +(which is the format used inside key files), +or 'Day Mon DD HH:MM:SS YYYY' (as printed by ``dnssec-settime -p``), +or UNIX epoch time (as printed by ``dnssec-settime -up``), +or the literal ``now``. + +The argument can be followed by ``+`` or ``-`` and an offset from the +given time. The literal ``now`` can be omitted before an offset. The +offset can be followed by one of the suffixes ``y``, ``mo``, ``w``, +``d``, ``h``, or ``mi``, so that it is computed in years (defined as +365 24-hour days, ignoring leap years), months (defined as 30 24-hour +days), weeks, days, hours, or minutes, respectively. Without a suffix, +the offset is computed in seconds. + +To unset a date, use ``none``, ``never``, or ``unset``. + +All these formats are case-insensitive. + +.. option:: -P date/offset + + This option sets the date on which a key is to be published to the zone. After + that date, the key is included in the zone but is not used + to sign it. + + .. program:: dnssec-settime -P + .. option:: ds date/offset + + This option sets the date on which DS records that match this key have been + seen in the parent zone. + + .. option:: sync date/offset + + This option sets the date on which CDS and CDNSKEY records that match this key + are to be published to the zone. + +.. program:: dnssec-settime + +.. option:: -A date/offset + + This option sets the date on which the key is to be activated. After that date, + the key is included in the zone and used to sign it. + +.. option:: -R date/offset + + This option sets the date on which the key is to be revoked. After that date, the + key is flagged as revoked. It is included in the zone and + is used to sign it. + +.. option:: -I date/offset + + This option sets the date on which the key is to be retired. After that date, the + key is still included in the zone, but it is not used to + sign it. + +.. option:: -D date/offset + + This option sets the date on which the key is to be deleted. After that date, the + key is no longer included in the zone. (However, it may remain in the key + repository.) + + .. program:: dnssec-settime -D + .. option:: ds date/offset + + This option sets the date on which the DS records that match this key have + been seen removed from the parent zone. + + .. option:: sync date/offset + + This option sets the date on which the CDS and CDNSKEY records that match this + key are to be deleted. + +.. program:: dnssec-settime + +.. option:: -S predecessor key + + This option selects a key for which the key being modified is an explicit + successor. The name, algorithm, size, and type of the predecessor key + must exactly match those of the key being modified. The activation + date of the successor key is set to the inactivation date of the + predecessor. The publication date is set to the activation date + minus the prepublication interval, which defaults to 30 days. + +.. option:: -i interval + + This option sets the prepublication interval for a key. If set, then the + publication and activation dates must be separated by at least this + much time. If the activation date is specified but the publication + date is not, the publication date defaults to this much time + before the activation date; conversely, if the publication date is + specified but not the activation date, activation is set to + this much time after publication. + + If the key is being created as an explicit successor to another key, + then the default prepublication interval is 30 days; otherwise it is + zero. + + As with date offsets, if the argument is followed by one of the + suffixes ``y``, ``mo``, ``w``, ``d``, ``h``, or ``mi``, the interval is + measured in years, months, weeks, days, hours, or minutes, + respectively. Without a suffix, the interval is measured in seconds. + +Key State Options +~~~~~~~~~~~~~~~~~ + +To test dnssec-policy it may be necessary to construct keys with artificial +state information; these options are used by the testing framework for that +purpose, but should never be used in production. + +Known key states are HIDDEN, RUMOURED, OMNIPRESENT, and UNRETENTIVE. + +.. option:: -s + + This option indicates that when setting key timing data, the state file should also be updated. + +.. option:: -g state + + This option sets the goal state for this key. Must be HIDDEN or OMNIPRESENT. + +.. option:: -d state date/offset + + This option sets the DS state for this key as of the specified date, offset from the current date. + +.. option:: -k state date/offset + + This option sets the DNSKEY state for this key as of the specified date, offset from the current date. + +.. option:: -r state date/offset + + This option sets the RRSIG (KSK) state for this key as of the specified date, offset from the current date. + +.. option:: -z state date/offset + + This option sets the RRSIG (ZSK) state for this key as of the specified date, offset from the current date. + +Printing Options +~~~~~~~~~~~~~~~~ + +:program:`dnssec-settime` can also be used to print the timing metadata +associated with a key. + +.. option:: -u + + This option indicates that times should be printed in Unix epoch format. + +.. option:: -p C/P/Pds/Psync/A/R/I/D/Dds/Dsync/all + + This option prints a specific metadata value or set of metadata values. + The :option:`-p` option may be followed by one or more of the following letters or + strings to indicate which value or values to print: ``C`` for the + creation date, ``P`` for the publication date, ``Pds` for the DS publication + date, ``Psync`` for the CDS and CDNSKEY publication date, ``A`` for the + activation date, ``R`` for the revocation date, ``I`` for the inactivation + date, ``D`` for the deletion date, ``Dds`` for the DS deletion date, + and ``Dsync`` for the CDS and CDNSKEY deletion date. To print all of the + metadata, use ``all``. + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, :iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, +:rfc:`5011`. diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c new file mode 100644 index 0000000..f52457c --- /dev/null +++ b/bin/dnssec/dnssec-signzone.c @@ -0,0 +1,4165 @@ +/* + * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + * + * Portions Copyright (C) Network Associates, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-signzone"; + +typedef struct hashlist hashlist_t; + +static int nsec_datatype = dns_rdatatype_nsec; + +#define check_dns_dbiterator_current(result) \ + check_result((result == DNS_R_NEWORIGIN) ? ISC_R_SUCCESS : result, \ + "dns_dbiterator_current()") + +#define IS_NSEC3 (nsec_datatype == dns_rdatatype_nsec3) +#define OPTOUT(x) (((x)&DNS_NSEC3FLAG_OPTOUT) != 0) + +#define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0) + +#define BUFSIZE 2048 +#define MAXDSKEYS 8 + +#define SIGNER_EVENTCLASS ISC_EVENTCLASS(0x4453) +#define SIGNER_EVENT_WRITE (SIGNER_EVENTCLASS + 0) +#define SIGNER_EVENT_WORK (SIGNER_EVENTCLASS + 1) + +#define SOA_SERIAL_KEEP 0 +#define SOA_SERIAL_INCREMENT 1 +#define SOA_SERIAL_UNIXTIME 2 +#define SOA_SERIAL_DATE 3 + +typedef struct signer_event sevent_t; +struct signer_event { + ISC_EVENT_COMMON(sevent_t); + dns_fixedname_t *fname; + dns_dbnode_t *node; +}; + +static dns_dnsseckeylist_t keylist; +static unsigned int keycount = 0; +static isc_rwlock_t keylist_lock; +static isc_stdtime_t starttime = 0, endtime = 0, dnskey_endtime = 0, now; +static int cycle = -1; +static int jitter = 0; +static bool tryverify = false; +static bool printstats = false; +static isc_mem_t *mctx = NULL; +static dns_ttl_t zone_soa_min_ttl; +static dns_ttl_t soa_ttl; +static FILE *outfp = NULL; +static char *tempfile = NULL; +static const dns_master_style_t *masterstyle; +static dns_masterformat_t inputformat = dns_masterformat_text; +static dns_masterformat_t outputformat = dns_masterformat_text; +static uint32_t rawversion = 1, serialnum = 0; +static bool snset = false; +static unsigned int nsigned = 0, nretained = 0, ndropped = 0; +static unsigned int nverified = 0, nverifyfailed = 0; +static const char *directory = NULL, *dsdir = NULL; +static isc_mutex_t namelock, statslock; +static isc_nm_t *netmgr = NULL; +static isc_taskmgr_t *taskmgr = NULL; +static dns_db_t *gdb; /* The database */ +static dns_dbversion_t *gversion; /* The database version */ +static dns_dbiterator_t *gdbiter; /* The database iterator */ +static dns_rdataclass_t gclass; /* The class */ +static dns_name_t *gorigin; /* The database origin */ +static int nsec3flags = 0; +static dns_iterations_t nsec3iter = 0U; +static unsigned char saltbuf[255]; +static unsigned char *gsalt = saltbuf; +static size_t salt_length = 0; +static isc_task_t *main_task = NULL; +static unsigned int ntasks = 0; +static atomic_bool shuttingdown; +static atomic_bool finished; +static bool nokeys = false; +static bool removefile = false; +static bool generateds = false; +static bool ignore_kskflag = false; +static bool keyset_kskonly = false; +static dns_master_style_t *dsstyle = NULL; +static unsigned int serialformat = SOA_SERIAL_KEEP; +static unsigned int hash_length = 0; +static bool unknownalg = false; +static bool disable_zone_check = false; +static bool update_chain = false; +static bool set_keyttl = false; +static dns_ttl_t keyttl; +static bool smartsign = false; +static bool remove_orphansigs = false; +static bool remove_inactkeysigs = false; +static bool output_dnssec_only = false; +static bool output_stdout = false; +static bool set_maxttl = false; +static dns_ttl_t maxttl = 0; +static bool no_max_check = false; + +#define INCSTAT(counter) \ + if (printstats) { \ + LOCK(&statslock); \ + counter++; \ + UNLOCK(&statslock); \ + } + +static void +sign(isc_task_t *task, isc_event_t *event); + +/*% + * Store a copy of 'name' in 'fzonecut' and return a pointer to that copy. + */ +static dns_name_t * +savezonecut(dns_fixedname_t *fzonecut, dns_name_t *name) { + dns_name_t *result; + + result = dns_fixedname_initname(fzonecut); + dns_name_copy(name, result); + + return (result); +} + +static void +dumpnode(dns_name_t *name, dns_dbnode_t *node) { + dns_rdataset_t rds; + dns_rdatasetiter_t *iter = NULL; + isc_buffer_t *buffer = NULL; + isc_region_t r; + isc_result_t result; + unsigned bufsize = 4096; + + if (outputformat != dns_masterformat_text) { + return; + } + + if (!output_dnssec_only) { + result = dns_master_dumpnodetostream(mctx, gdb, gversion, node, + name, masterstyle, outfp); + check_result(result, "dns_master_dumpnodetostream"); + return; + } + + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &iter); + check_result(result, "dns_db_allrdatasets"); + + dns_rdataset_init(&rds); + + isc_buffer_allocate(mctx, &buffer, bufsize); + + for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(iter)) + { + dns_rdatasetiter_current(iter, &rds); + + if (rds.type != dns_rdatatype_rrsig && + rds.type != dns_rdatatype_nsec && + rds.type != dns_rdatatype_nsec3 && + rds.type != dns_rdatatype_nsec3param && + (!smartsign || rds.type != dns_rdatatype_dnskey)) + { + dns_rdataset_disassociate(&rds); + continue; + } + + for (;;) { + result = dns_master_rdatasettotext( + name, &rds, masterstyle, NULL, buffer); + if (result != ISC_R_NOSPACE) { + break; + } + + bufsize <<= 1; + isc_buffer_free(&buffer); + isc_buffer_allocate(mctx, &buffer, bufsize); + } + check_result(result, "dns_master_rdatasettotext"); + + isc_buffer_usedregion(buffer, &r); + result = isc_stdio_write(r.base, 1, r.length, outfp, NULL); + check_result(result, "isc_stdio_write"); + isc_buffer_clear(buffer); + + dns_rdataset_disassociate(&rds); + } + + isc_buffer_free(&buffer); + dns_rdatasetiter_destroy(&iter); +} + +/*% + * Sign the given RRset with given key, and add the signature record to the + * given tuple. + */ +static void +signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dst_key_t *key, + dns_ttl_t ttl, dns_diff_t *add, const char *logmsg) { + isc_result_t result; + isc_stdtime_t jendtime, expiry; + char keystr[DST_KEY_FORMATSIZE]; + dns_rdata_t trdata = DNS_RDATA_INIT; + unsigned char array[BUFSIZE]; + isc_buffer_t b; + dns_difftuple_t *tuple; + + dst_key_format(key, keystr, sizeof(keystr)); + vbprintf(1, "\t%s %s\n", logmsg, keystr); + + if (rdataset->type == dns_rdatatype_dnskey) { + expiry = dnskey_endtime; + } else { + expiry = endtime; + } + + jendtime = (jitter != 0) ? expiry - isc_random_uniform(jitter) : expiry; + isc_buffer_init(&b, array, sizeof(array)); + result = dns_dnssec_sign(name, rdataset, key, &starttime, &jendtime, + mctx, &b, &trdata); + if (result != ISC_R_SUCCESS) { + fatal("dnskey '%s' failed to sign data: %s", keystr, + isc_result_totext(result)); + } + INCSTAT(nsigned); + + if (tryverify) { + result = dns_dnssec_verify(name, rdataset, key, true, 0, mctx, + &trdata, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { + vbprintf(3, "\tsignature verified\n"); + INCSTAT(nverified); + } else { + vbprintf(3, "\tsignature failed to verify\n"); + INCSTAT(nverifyfailed); + } + } + + tuple = NULL; + result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name, ttl, + &trdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(add, &tuple); +} + +static bool +issigningkey(dns_dnsseckey_t *key) { + return (key->force_sign || key->hint_sign); +} + +static bool +ispublishedkey(dns_dnsseckey_t *key) { + return ((key->force_publish || key->hint_publish) && !key->hint_remove); +} + +static bool +iszonekey(dns_dnsseckey_t *key) { + return (dns_name_equal(dst_key_name(key->key), gorigin) && + dst_key_iszonekey(key->key)); +} + +static bool +isksk(dns_dnsseckey_t *key) { + return (key->ksk); +} + +static bool +iszsk(dns_dnsseckey_t *key) { + return (ignore_kskflag || !key->ksk); +} + +/*% + * Find the key that generated an RRSIG, if it is in the key list. If + * so, return a pointer to it, otherwise return NULL. + * + * No locking is performed here, this must be done by the caller. + */ +static dns_dnsseckey_t * +keythatsigned_unlocked(dns_rdata_rrsig_t *rrsig) { + dns_dnsseckey_t *key; + + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (rrsig->keyid == dst_key_id(key->key) && + rrsig->algorithm == dst_key_alg(key->key) && + dns_name_equal(&rrsig->signer, dst_key_name(key->key))) + { + return (key); + } + } + return (NULL); +} + +/*% + * Finds the key that generated a RRSIG, if possible. First look at the keys + * that we've loaded already, and then see if there's a key on disk. + */ +static dns_dnsseckey_t * +keythatsigned(dns_rdata_rrsig_t *rrsig) { + isc_result_t result; + dst_key_t *pubkey = NULL, *privkey = NULL; + dns_dnsseckey_t *key = NULL; + + RWLOCK(&keylist_lock, isc_rwlocktype_read); + key = keythatsigned_unlocked(rrsig); + RWUNLOCK(&keylist_lock, isc_rwlocktype_read); + if (key != NULL) { + return (key); + } + + /* + * We did not find the key in our list. Get a write lock now, since + * we may be modifying the bits. We could do the tryupgrade() dance, + * but instead just get a write lock and check once again to see if + * it is on our list. It's possible someone else may have added it + * after all. + */ + isc_rwlock_lock(&keylist_lock, isc_rwlocktype_write); + key = keythatsigned_unlocked(rrsig); + if (key != NULL) { + isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write); + return (key); + } + + result = dst_key_fromfile(&rrsig->signer, rrsig->keyid, + rrsig->algorithm, DST_TYPE_PUBLIC, directory, + mctx, &pubkey); + if (result != ISC_R_SUCCESS) { + isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write); + return (NULL); + } + + result = dst_key_fromfile( + &rrsig->signer, rrsig->keyid, rrsig->algorithm, + DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, directory, mctx, &privkey); + if (result == ISC_R_SUCCESS) { + dst_key_free(&pubkey); + result = dns_dnsseckey_create(mctx, &privkey, &key); + } else { + result = dns_dnsseckey_create(mctx, &pubkey, &key); + } + + if (result == ISC_R_SUCCESS) { + key->force_publish = false; + key->force_sign = false; + key->index = keycount++; + ISC_LIST_APPEND(keylist, key, link); + } + + isc_rwlock_unlock(&keylist_lock, isc_rwlocktype_write); + return (key); +} + +/*% + * Check to see if we expect to find a key at this name. If we see a RRSIG + * and can't find the signing key that we expect to find, we drop the rrsig. + * I'm not sure if this is completely correct, but it seems to work. + */ +static bool +expecttofindkey(dns_name_t *name) { + unsigned int options = DNS_DBFIND_NOWILD; + dns_fixedname_t fname; + isc_result_t result; + char namestr[DNS_NAME_FORMATSIZE]; + + dns_fixedname_init(&fname); + result = dns_db_find(gdb, name, gversion, dns_rdatatype_dnskey, options, + 0, NULL, dns_fixedname_name(&fname), NULL, NULL); + switch (result) { + case ISC_R_SUCCESS: + case DNS_R_NXDOMAIN: + case DNS_R_NXRRSET: + return (true); + case DNS_R_DELEGATION: + case DNS_R_CNAME: + case DNS_R_DNAME: + return (false); + default: + break; + } + dns_name_format(name, namestr, sizeof(namestr)); + fatal("failure looking for '%s DNSKEY' in database: %s", namestr, + isc_result_totext(result)); + UNREACHABLE(); + return (false); /* removes a warning */ +} + +static bool +setverifies(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, + dns_rdata_t *rrsig) { + isc_result_t result; + result = dns_dnssec_verify(name, set, key, false, 0, mctx, rrsig, NULL); + if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD) { + INCSTAT(nverified); + return (true); + } else { + INCSTAT(nverifyfailed); + return (false); + } +} + +/*% + * Signs a set. Goes through contortions to decide if each RRSIG should + * be dropped or retained, and then determines if any new SIGs need to + * be generated. + */ +static void +signset(dns_diff_t *del, dns_diff_t *add, dns_dbnode_t *node, dns_name_t *name, + dns_rdataset_t *set) { + dns_rdataset_t sigset; + dns_rdata_t sigrdata = DNS_RDATA_INIT; + dns_rdata_rrsig_t rrsig; + dns_dnsseckey_t *key; + isc_result_t result; + bool nosigs = false; + bool *wassignedby, *nowsignedby; + int arraysize; + dns_difftuple_t *tuple; + dns_ttl_t ttl; + int i; + char namestr[DNS_NAME_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; + char sigstr[SIG_FORMATSIZE]; + + dns_name_format(name, namestr, sizeof(namestr)); + dns_rdatatype_format(set->type, typestr, sizeof(typestr)); + + ttl = ISC_MIN(set->ttl, endtime - starttime); + + dns_rdataset_init(&sigset); + result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_rrsig, + set->type, 0, &sigset, NULL); + if (result == ISC_R_NOTFOUND) { + vbprintf(2, "no existing signatures for %s/%s\n", namestr, + typestr); + result = ISC_R_SUCCESS; + nosigs = true; + } + if (result != ISC_R_SUCCESS) { + fatal("failed while looking for '%s RRSIG %s': %s", namestr, + typestr, isc_result_totext(result)); + } + + vbprintf(1, "%s/%s:\n", namestr, typestr); + + arraysize = keycount; + if (!nosigs) { + arraysize += dns_rdataset_count(&sigset); + } + wassignedby = isc_mem_get(mctx, arraysize * sizeof(bool)); + nowsignedby = isc_mem_get(mctx, arraysize * sizeof(bool)); + + for (i = 0; i < arraysize; i++) { + wassignedby[i] = nowsignedby[i] = false; + } + + if (nosigs) { + result = ISC_R_NOMORE; + } else { + result = dns_rdataset_first(&sigset); + } + + while (result == ISC_R_SUCCESS) { + bool expired, future; + bool keep = false, resign = false; + + dns_rdataset_current(&sigset, &sigrdata); + + result = dns_rdata_tostruct(&sigrdata, &rrsig, NULL); + check_result(result, "dns_rdata_tostruct"); + + future = isc_serial_lt(now, rrsig.timesigned); + + key = keythatsigned(&rrsig); + sig_format(&rrsig, sigstr, sizeof(sigstr)); + expired = isc_serial_gt(now + cycle, rrsig.timeexpire); + + if (isc_serial_gt(rrsig.timesigned, rrsig.timeexpire)) { + /* rrsig is dropped and not replaced */ + vbprintf(2, + "\trrsig by %s dropped - " + "invalid validity period\n", + sigstr); + } else if (key == NULL && !future && + expecttofindkey(&rrsig.signer)) + { + /* rrsig is dropped and not replaced */ + vbprintf(2, + "\trrsig by %s dropped - " + "private dnskey not found\n", + sigstr); + } else if (key == NULL || future) { + keep = (!expired && !remove_orphansigs); + vbprintf(2, "\trrsig by %s %s - dnskey not found\n", + keep ? "retained" : "dropped", sigstr); + } else if (!dns_dnssec_keyactive(key->key, now) && + remove_inactkeysigs) + { + keep = false; + vbprintf(2, "\trrsig by %s dropped - key inactive\n", + sigstr); + } else if (issigningkey(key)) { + wassignedby[key->index] = true; + + if (!expired && rrsig.originalttl == set->ttl && + setverifies(name, set, key->key, &sigrdata)) + { + vbprintf(2, "\trrsig by %s retained\n", sigstr); + keep = true; + } else { + vbprintf(2, "\trrsig by %s dropped - %s\n", + sigstr, + expired ? "expired" + : rrsig.originalttl != set->ttl + ? "ttl change" + : "failed to " + "verify"); + resign = true; + } + } else if (!ispublishedkey(key) && remove_orphansigs) { + vbprintf(2, "\trrsig by %s dropped - dnskey removed\n", + sigstr); + } else if (iszonekey(key)) { + wassignedby[key->index] = true; + + if (!expired && rrsig.originalttl == set->ttl && + setverifies(name, set, key->key, &sigrdata)) + { + vbprintf(2, "\trrsig by %s retained\n", sigstr); + keep = true; + } else { + vbprintf(2, "\trrsig by %s dropped - %s\n", + sigstr, + expired ? "expired" + : rrsig.originalttl != set->ttl + ? "ttl change" + : "failed to " + "verify"); + } + } else if (!expired) { + vbprintf(2, "\trrsig by %s retained\n", sigstr); + keep = true; + } else { + vbprintf(2, "\trrsig by %s expired\n", sigstr); + } + + if (keep) { + if (key != NULL) { + nowsignedby[key->index] = true; + } + INCSTAT(nretained); + if (sigset.ttl != ttl) { + vbprintf(2, "\tfixing ttl %s\n", sigstr); + tuple = NULL; + result = dns_difftuple_create( + mctx, DNS_DIFFOP_DELRESIGN, name, + sigset.ttl, &sigrdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(del, &tuple); + result = dns_difftuple_create( + mctx, DNS_DIFFOP_ADDRESIGN, name, ttl, + &sigrdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(add, &tuple); + } + } else { + tuple = NULL; + vbprintf(2, "\tremoving signature by %s\n", sigstr); + result = dns_difftuple_create( + mctx, DNS_DIFFOP_DELRESIGN, name, sigset.ttl, + &sigrdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(del, &tuple); + INCSTAT(ndropped); + } + + if (resign) { + INSIST(!keep); + + signwithkey(name, set, key->key, ttl, add, + "resigning with dnskey"); + nowsignedby[key->index] = true; + } + + dns_rdata_reset(&sigrdata); + dns_rdata_freestruct(&rrsig); + result = dns_rdataset_next(&sigset); + } + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + + check_result(result, "dns_rdataset_first/next"); + if (dns_rdataset_isassociated(&sigset)) { + dns_rdataset_disassociate(&sigset); + } + + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (nowsignedby[key->index]) { + continue; + } + + if (!issigningkey(key)) { + continue; + } + + if ((set->type == dns_rdatatype_cds || + set->type == dns_rdatatype_cdnskey || + set->type == dns_rdatatype_dnskey) && + dns_name_equal(name, gorigin)) + { + bool have_ksk; + dns_dnsseckey_t *curr; + + have_ksk = isksk(key); + for (curr = ISC_LIST_HEAD(keylist); curr != NULL; + curr = ISC_LIST_NEXT(curr, link)) + { + if (dst_key_alg(key->key) != + dst_key_alg(curr->key)) + { + continue; + } + if (REVOKE(curr->key)) { + continue; + } + if (isksk(curr)) { + have_ksk = true; + } + } + if (isksk(key) || !have_ksk || + (iszsk(key) && !keyset_kskonly)) + { + signwithkey(name, set, key->key, ttl, add, + "signing with dnskey"); + } + } else if (iszsk(key)) { + /* + * Sign with the ZSK unless there is a predecessor + * key that already signs this RRset. + */ + bool have_pre_sig = false; + dns_dnsseckey_t *curr; + uint32_t pre; + isc_result_t ret = dst_key_getnum( + key->key, DST_NUM_PREDECESSOR, &pre); + if (ret == ISC_R_SUCCESS) { + /* + * This key has a predecessor, look for the + * corresponding key in the keylist. The + * key we are looking for must be: + * - From the same cryptographic algorithm. + * - Have the ZSK type (iszsk). + * - Have key ID equal to the predecessor id. + * - Have a successor that matches 'key' id. + */ + for (curr = ISC_LIST_HEAD(keylist); + curr != NULL; + curr = ISC_LIST_NEXT(curr, link)) + { + uint32_t suc; + + if (dst_key_alg(key->key) != + dst_key_alg(curr->key) || + !iszsk(curr) || + dst_key_id(curr->key) != pre) + { + continue; + } + ret = dst_key_getnum(curr->key, + DST_NUM_SUCCESSOR, + &suc); + if (ret != ISC_R_SUCCESS || + dst_key_id(key->key) != suc) + { + continue; + } + + /* + * curr is the predecessor we were + * looking for. Check if this key + * signs this RRset. + */ + if (nowsignedby[curr->index]) { + have_pre_sig = true; + } + } + } + + /* + * If we have a signature of a predecessor key, + * skip signing with this key. + */ + if (!have_pre_sig) { + signwithkey(name, set, key->key, ttl, add, + "signing with dnskey"); + } + } + } + + isc_mem_put(mctx, wassignedby, arraysize * sizeof(bool)); + isc_mem_put(mctx, nowsignedby, arraysize * sizeof(bool)); +} + +struct hashlist { + unsigned char *hashbuf; + size_t entries; + size_t size; + size_t length; +}; + +static void +hashlist_init(hashlist_t *l, unsigned int nodes, unsigned int length) { + l->entries = 0; + l->length = length + 1; + + if (nodes != 0) { + l->size = nodes; + l->hashbuf = malloc(l->size * l->length); + if (l->hashbuf == NULL) { + l->size = 0; + } + } else { + l->size = 0; + l->hashbuf = NULL; + } +} + +static void +hashlist_free(hashlist_t *l) { + if (l->hashbuf) { + free(l->hashbuf); + l->hashbuf = NULL; + l->entries = 0; + l->length = 0; + l->size = 0; + } +} + +static void +hashlist_add(hashlist_t *l, const unsigned char *hash, size_t len) { + REQUIRE(len <= l->length); + + if (l->entries == l->size) { + l->size = l->size * 2 + 100; + l->hashbuf = realloc(l->hashbuf, l->size * l->length); + if (l->hashbuf == NULL) { + fatal("unable to grow hashlist: out of memory"); + } + } + memset(l->hashbuf + l->entries * l->length, 0, l->length); + memmove(l->hashbuf + l->entries * l->length, hash, len); + l->entries++; +} + +static void +hashlist_add_dns_name(hashlist_t *l, + /*const*/ dns_name_t *name, unsigned int hashalg, + unsigned int iterations, const unsigned char *salt, + size_t salt_len, bool speculative) { + char nametext[DNS_NAME_FORMATSIZE]; + unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1]; + unsigned int len; + size_t i; + + len = isc_iterated_hash(hash, hashalg, iterations, salt, (int)salt_len, + name->ndata, name->length); + if (verbose) { + dns_name_format(name, nametext, sizeof nametext); + for (i = 0; i < len; i++) { + fprintf(stderr, "%02x", hash[i]); + } + fprintf(stderr, " %s\n", nametext); + } + hash[len++] = speculative ? 1 : 0; + hashlist_add(l, hash, len); +} + +static int +hashlist_comp(const void *a, const void *b) { + return (memcmp(a, b, hash_length + 1)); +} + +static void +hashlist_sort(hashlist_t *l) { + INSIST(l->hashbuf != NULL || l->length == 0); + if (l->length > 0) { + qsort(l->hashbuf, l->entries, l->length, hashlist_comp); + } +} + +static bool +hashlist_hasdup(hashlist_t *l) { + unsigned char *current; + unsigned char *next = l->hashbuf; + size_t entries = l->entries; + + /* + * Skip initial speculative wild card hashes. + */ + while (entries > 0U && next[l->length - 1] != 0U) { + next += l->length; + entries--; + } + + current = next; + while (entries-- > 1U) { + next += l->length; + if (next[l->length - 1] != 0) { + continue; + } + if (isc_safe_memequal(current, next, l->length - 1)) { + return (true); + } + current = next; + } + return (false); +} + +static const unsigned char * +hashlist_findnext(const hashlist_t *l, + const unsigned char hash[NSEC3_MAX_HASH_LENGTH]) { + size_t entries = l->entries; + const unsigned char *next = bsearch(hash, l->hashbuf, l->entries, + l->length, hashlist_comp); + INSIST(next != NULL); + + do { + if (next < l->hashbuf + (l->entries - 1) * l->length) { + next += l->length; + } else { + next = l->hashbuf; + } + if (next[l->length - 1] == 0) { + break; + } + } while (entries-- > 1U); + INSIST(entries != 0U); + return (next); +} + +static bool +hashlist_exists(const hashlist_t *l, + const unsigned char hash[NSEC3_MAX_HASH_LENGTH]) { + if (bsearch(hash, l->hashbuf, l->entries, l->length, hashlist_comp)) { + return (true); + } else { + return (false); + } +} + +static void +addnowildcardhash(hashlist_t *l, + /*const*/ dns_name_t *name, unsigned int hashalg, + unsigned int iterations, const unsigned char *salt, + size_t salt_len) { + dns_fixedname_t fixed; + dns_name_t *wild; + dns_dbnode_t *node = NULL; + isc_result_t result; + char namestr[DNS_NAME_FORMATSIZE]; + + wild = dns_fixedname_initname(&fixed); + + result = dns_name_concatenate(dns_wildcardname, name, wild, NULL); + if (result == ISC_R_NOSPACE) { + return; + } + check_result(result, "addnowildcardhash: dns_name_concatenate()"); + + result = dns_db_findnode(gdb, wild, false, &node); + if (result == ISC_R_SUCCESS) { + dns_db_detachnode(gdb, &node); + return; + } + + if (verbose) { + dns_name_format(wild, namestr, sizeof(namestr)); + fprintf(stderr, "adding no-wildcardhash for %s\n", namestr); + } + + hashlist_add_dns_name(l, wild, hashalg, iterations, salt, salt_len, + true); +} + +static void +opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass, + dns_db_t **dbp) { + char filename[PATH_MAX]; + isc_buffer_t b; + isc_result_t result; + + isc_buffer_init(&b, filename, sizeof(filename)); + if (dsdir != NULL) { + /* allow room for a trailing slash */ + if (strlen(dsdir) >= isc_buffer_availablelength(&b)) { + fatal("path '%s' is too long", dsdir); + } + isc_buffer_putstr(&b, dsdir); + if (dsdir[strlen(dsdir) - 1] != '/') { + isc_buffer_putstr(&b, "/"); + } + } + if (strlen(prefix) > isc_buffer_availablelength(&b)) { + fatal("path '%s' is too long", dsdir); + } + isc_buffer_putstr(&b, prefix); + result = dns_name_tofilenametext(name, false, &b); + check_result(result, "dns_name_tofilenametext()"); + if (isc_buffer_availablelength(&b) == 0) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namestr, sizeof(namestr)); + fatal("name '%s' is too long", namestr); + } + isc_buffer_putuint8(&b, 0); + + result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone, + rdclass, 0, NULL, dbp); + check_result(result, "dns_db_create()"); + + result = dns_db_load(*dbp, filename, inputformat, DNS_MASTER_HINT); + if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { + dns_db_detach(dbp); + } +} + +/*% + * Load the DS set for a child zone, if a dsset-* file can be found. + * If not, try to find a keyset-* file from an earlier version of + * dnssec-signzone, and build DS records from that. + */ +static isc_result_t +loadds(dns_name_t *name, uint32_t ttl, dns_rdataset_t *dsset) { + dns_db_t *db = NULL; + dns_dbversion_t *ver = NULL; + dns_dbnode_t *node = NULL; + isc_result_t result; + dns_rdataset_t keyset; + dns_rdata_t key, ds; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL; + + opendb("dsset-", name, gclass, &db); + if (db != NULL) { + result = dns_db_findnode(db, name, false, &node); + if (result == ISC_R_SUCCESS) { + dns_rdataset_init(dsset); + result = dns_db_findrdataset(db, node, NULL, + dns_rdatatype_ds, 0, 0, + dsset, NULL); + dns_db_detachnode(db, &node); + if (result == ISC_R_SUCCESS) { + vbprintf(2, "found DS records\n"); + dsset->ttl = ttl; + dns_db_detach(&db); + return (result); + } + } + dns_db_detach(&db); + } + + /* No DS records found; try again, looking for DNSKEY records */ + opendb("keyset-", name, gclass, &db); + if (db == NULL) { + return (ISC_R_NOTFOUND); + } + + result = dns_db_findnode(db, name, false, &node); + if (result != ISC_R_SUCCESS) { + dns_db_detach(&db); + return (result); + } + + dns_rdataset_init(&keyset); + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_dnskey, 0, 0, + &keyset, NULL); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(db, &node); + dns_db_detach(&db); + return (result); + } + vbprintf(2, "found DNSKEY records\n"); + + result = dns_db_newversion(db, &ver); + check_result(result, "dns_db_newversion"); + dns_diff_init(mctx, &diff); + + for (result = dns_rdataset_first(&keyset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(&keyset)) + { + dns_rdata_init(&key); + dns_rdata_init(&ds); + dns_rdataset_current(&keyset, &key); + result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA256, + dsbuf, &ds); + check_result(result, "dns_ds_buildrdata"); + + result = dns_difftuple_create(mctx, DNS_DIFFOP_ADDRESIGN, name, + ttl, &ds, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(&diff, &tuple); + } + + result = dns_diff_apply(&diff, db, ver); + check_result(result, "dns_diff_apply"); + dns_diff_clear(&diff); + + dns_db_closeversion(db, &ver, true); + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, 0, + dsset, NULL); + check_result(result, "dns_db_findrdataset"); + + dns_rdataset_disassociate(&keyset); + dns_db_detachnode(db, &node); + dns_db_detach(&db); + return (result); +} + +static bool +secure(dns_name_t *name, dns_dbnode_t *node) { + dns_rdataset_t dsset; + isc_result_t result; + + if (dns_name_equal(name, gorigin)) { + return (false); + } + + dns_rdataset_init(&dsset); + result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ds, 0, + 0, &dsset, NULL); + if (dns_rdataset_isassociated(&dsset)) { + dns_rdataset_disassociate(&dsset); + } + + return (result == ISC_R_SUCCESS); +} + +static bool +is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin, + dns_name_t *name, dns_dbnode_t *node, uint32_t *ttlp) { + dns_rdataset_t nsset; + isc_result_t result; + + if (dns_name_equal(name, origin)) { + return (false); + } + + dns_rdataset_init(&nsset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, 0, 0, + &nsset, NULL); + if (dns_rdataset_isassociated(&nsset)) { + if (ttlp != NULL) { + *ttlp = nsset.ttl; + } + dns_rdataset_disassociate(&nsset); + } + + return ((result == ISC_R_SUCCESS)); +} + +/*% + * Return true if version 'ver' of database 'db' contains a DNAME RRset at + * 'node'; return false otherwise. + */ +static bool +has_dname(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node) { + dns_rdataset_t dnameset; + isc_result_t result; + + dns_rdataset_init(&dnameset); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_dname, 0, 0, + &dnameset, NULL); + if (dns_rdataset_isassociated(&dnameset)) { + dns_rdataset_disassociate(&dnameset); + } + + return ((result == ISC_R_SUCCESS)); +} + +/*% + * Signs all records at a name. + */ +static void +signname(dns_dbnode_t *node, dns_name_t *name) { + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdatasetiter_t *rdsiter; + bool isdelegation = false; + dns_diff_t del, add; + char namestr[DNS_NAME_FORMATSIZE]; + + dns_rdataset_init(&rdataset); + dns_name_format(name, namestr, sizeof(namestr)); + + /* + * Determine if this is a delegation point. + */ + if (is_delegation(gdb, gversion, gorigin, name, node, NULL)) { + isdelegation = true; + } + + /* + * Now iterate through the rdatasets. + */ + dns_diff_init(mctx, &del); + dns_diff_init(mctx, &add); + rdsiter = NULL; + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + + /* If this is a RRSIG set, skip it. */ + if (rdataset.type == dns_rdatatype_rrsig) { + goto skip; + } + + /* + * If this name is a delegation point, skip all records + * except NSEC and DS sets. Otherwise check that there + * isn't a DS record. + */ + if (isdelegation) { + if (rdataset.type != nsec_datatype && + rdataset.type != dns_rdatatype_ds) + { + goto skip; + } + } else if (rdataset.type == dns_rdatatype_ds) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namebuf, sizeof(namebuf)); + fatal("'%s': found DS RRset without NS RRset\n", + namebuf); + } + + signset(&del, &add, node, name, &rdataset); + + skip: + dns_rdataset_disassociate(&rdataset); + result = dns_rdatasetiter_next(rdsiter); + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration for name '%s' failed: %s", namestr, + isc_result_totext(result)); + } + + dns_rdatasetiter_destroy(&rdsiter); + + result = dns_diff_applysilently(&del, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to delete SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + + result = dns_diff_applysilently(&add, gdb, gversion); + if (result != ISC_R_SUCCESS) { + fatal("failed to add SIGs at node '%s': %s", namestr, + isc_result_totext(result)); + } + + dns_diff_clear(&del); + dns_diff_clear(&add); +} + +/* + * See if the node contains any non RRSIG/NSEC records and report to + * caller. Clean out extraneous RRSIG records for node. + */ +static bool +active_node(dns_dbnode_t *node) { + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdatasetiter_t *rdsiter2 = NULL; + bool active = false; + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdatatype_t type; + dns_rdatatype_t covers; + bool found; + + dns_rdataset_init(&rdataset); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + dns_rdatasetiter_current(rdsiter, &rdataset); + if (rdataset.type != dns_rdatatype_nsec && + rdataset.type != dns_rdatatype_nsec3 && + rdataset.type != dns_rdatatype_rrsig) + { + active = true; + } + dns_rdataset_disassociate(&rdataset); + if (!active) { + result = dns_rdatasetiter_next(rdsiter); + } else { + result = ISC_R_NOMORE; + } + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + + if (!active && nsec_datatype == dns_rdatatype_nsec) { + /*% + * The node is empty of everything but NSEC / RRSIG records. + */ + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + result = dns_db_deleterdataset(gdb, node, gversion, + rdataset.type, + rdataset.covers); + check_result(result, "dns_db_deleterdataset()"); + dns_rdataset_disassociate(&rdataset); + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + } else { + /* + * Delete RRSIGs for types that no longer exist. + */ + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, + &rdsiter2); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + type = rdataset.type; + covers = rdataset.covers; + dns_rdataset_disassociate(&rdataset); + /* + * Delete the NSEC chain if we are signing with + * NSEC3. + */ + if (nsec_datatype == dns_rdatatype_nsec3 && + (type == dns_rdatatype_nsec || + covers == dns_rdatatype_nsec)) + { + result = dns_db_deleterdataset( + gdb, node, gversion, type, covers); + check_result(result, "dns_db_deleterdataset(" + "nsec/rrsig)"); + continue; + } + if (type != dns_rdatatype_rrsig) { + continue; + } + found = false; + for (result = dns_rdatasetiter_first(rdsiter2); + !found && result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter2)) + { + dns_rdatasetiter_current(rdsiter2, &rdataset); + if (rdataset.type == covers) { + found = true; + } + dns_rdataset_disassociate(&rdataset); + } + if (!found) { + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + result = dns_db_deleterdataset( + gdb, node, gversion, type, covers); + check_result(result, "dns_db_deleterdataset(" + "rrsig)"); + } else if (result != ISC_R_NOMORE && + result != ISC_R_SUCCESS) + { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + dns_rdatasetiter_destroy(&rdsiter2); + } + dns_rdatasetiter_destroy(&rdsiter); + + return (active); +} + +/*% + * Extracts the minimum TTL from the SOA record, and the SOA record's TTL. + */ +static void +get_soa_ttls(void) { + dns_rdataset_t soaset; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + + name = dns_fixedname_initname(&fname); + dns_rdataset_init(&soaset); + result = dns_db_find(gdb, gorigin, gversion, dns_rdatatype_soa, 0, 0, + NULL, name, &soaset, NULL); + if (result != ISC_R_SUCCESS) { + fatal("failed to find an SOA at the zone apex: %s", + isc_result_totext(result)); + } + + result = dns_rdataset_first(&soaset); + check_result(result, "dns_rdataset_first"); + dns_rdataset_current(&soaset, &rdata); + soa_ttl = soaset.ttl; + zone_soa_min_ttl = ISC_MIN(dns_soa_getminimum(&rdata), soa_ttl); + if (set_maxttl) { + zone_soa_min_ttl = ISC_MIN(zone_soa_min_ttl, maxttl); + soa_ttl = ISC_MIN(soa_ttl, maxttl); + } + dns_rdataset_disassociate(&soaset); +} + +/*% + * Increment (or set if nonzero) the SOA serial + */ +static isc_result_t +setsoaserial(uint32_t serial, dns_updatemethod_t method) { + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + uint32_t old_serial, new_serial = 0; + dns_updatemethod_t used = dns_updatemethod_none; + + result = dns_db_getoriginnode(gdb, &node); + if (result != ISC_R_SUCCESS) { + return (result); + } + + dns_rdataset_init(&rdataset); + + result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_soa, 0, + 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_rdataset_first(&rdataset); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_rdataset_current(&rdataset, &rdata); + + old_serial = dns_soa_getserial(&rdata); + + if (method == dns_updatemethod_date || + method == dns_updatemethod_unixtime) + { + new_serial = dns_update_soaserial(old_serial, method, &used); + } else if (serial != 0 || method == dns_updatemethod_none) { + /* Set SOA serial to the value provided. */ + new_serial = serial; + used = method; + } else { + new_serial = dns_update_soaserial(old_serial, method, &used); + } + + if (method != used) { + fprintf(stderr, + "%s: warning: Serial number would not advance, " + "using increment method instead\n", + program); + } + + /* If the new serial is not likely to cause a zone transfer + * (a/ixfr) from servers having the old serial, warn the user. + * + * RFC1982 section 7 defines the maximum increment to be + * (2^(32-1))-1. Using u_int32_t arithmetic, we can do a single + * comparison. (5 - 6 == (2^32)-1, not negative-one) + */ + if (new_serial == old_serial || (new_serial - old_serial) > 0x7fffffffU) + { + fprintf(stderr, + "%s: warning: Serial number not advanced, " + "zone may not transfer\n", + program); + } + + dns_soa_setserial(new_serial, &rdata); + + result = dns_db_deleterdataset(gdb, node, gversion, dns_rdatatype_soa, + 0); + check_result(result, "dns_db_deleterdataset"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_db_addrdataset(gdb, node, gversion, 0, &rdataset, 0, NULL); + check_result(result, "dns_db_addrdataset"); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + +cleanup: + dns_rdataset_disassociate(&rdataset); + if (node != NULL) { + dns_db_detachnode(gdb, &node); + } + dns_rdata_reset(&rdata); + + return (result); +} + +/*% + * Delete any RRSIG records at a node. + */ +static void +cleannode(dns_db_t *db, dns_dbversion_t *dbversion, dns_dbnode_t *node) { + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdataset_t set; + isc_result_t result, dresult; + + if (outputformat != dns_masterformat_text || !disable_zone_check) { + return; + } + + dns_rdataset_init(&set); + result = dns_db_allrdatasets(db, node, dbversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets"); + result = dns_rdatasetiter_first(rdsiter); + while (result == ISC_R_SUCCESS) { + bool destroy = false; + dns_rdatatype_t covers = 0; + dns_rdatasetiter_current(rdsiter, &set); + if (set.type == dns_rdatatype_rrsig) { + covers = set.covers; + destroy = true; + } + dns_rdataset_disassociate(&set); + result = dns_rdatasetiter_next(rdsiter); + if (destroy) { + dresult = dns_db_deleterdataset(db, node, dbversion, + dns_rdatatype_rrsig, + covers); + check_result(dresult, "dns_db_deleterdataset"); + } + } + if (result != ISC_R_NOMORE) { + fatal("rdataset iteration failed: %s", + isc_result_totext(result)); + } + dns_rdatasetiter_destroy(&rdsiter); +} + +/*% + * Set up the iterator and global state before starting the tasks. + */ +static void +presign(void) { + isc_result_t result; + + gdbiter = NULL; + result = dns_db_createiterator(gdb, 0, &gdbiter); + check_result(result, "dns_db_createiterator()"); +} + +/*% + * Clean up the iterator and global state after the tasks complete. + */ +static void +postsign(void) { + dns_dbiterator_destroy(&gdbiter); +} + +/*% + * Sign the apex of the zone. + * Note the origin may not be the first node if there are out of zone + * records. + */ +static void +signapex(void) { + dns_dbnode_t *node = NULL; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + + name = dns_fixedname_initname(&fixed); + result = dns_dbiterator_seek(gdbiter, gorigin); + check_result(result, "dns_dbiterator_seek()"); + result = dns_dbiterator_current(gdbiter, &node, name); + check_dns_dbiterator_current(result); + signname(node, name); + dumpnode(name, node); + cleannode(gdb, gversion, node); + dns_db_detachnode(gdb, &node); + result = dns_dbiterator_first(gdbiter); + if (result == ISC_R_NOMORE) { + atomic_store(&finished, true); + } else if (result != ISC_R_SUCCESS) { + fatal("failure iterating database: %s", + isc_result_totext(result)); + } +} + +/*% + * Assigns a node to a worker thread. This is protected by the main task's + * lock. + */ +static void +assignwork(isc_task_t *task, isc_task_t *worker) { + dns_fixedname_t *fname; + dns_name_t *name; + dns_dbnode_t *node; + sevent_t *sevent; + dns_rdataset_t nsec; + bool found; + isc_result_t result; + static dns_name_t *zonecut = NULL; /* Protected by namelock. */ + static dns_fixedname_t fzonecut; /* Protected by namelock. */ + static unsigned int ended = 0; /* Protected by namelock. */ + + if (atomic_load(&shuttingdown)) { + return; + } + + LOCK(&namelock); + if (atomic_load(&finished)) { + ended++; + if (ended == ntasks) { + isc_task_detach(&task); + isc_app_shutdown(); + } + goto unlock; + } + + fname = isc_mem_get(mctx, sizeof(dns_fixedname_t)); + name = dns_fixedname_initname(fname); + node = NULL; + found = false; + while (!found) { + result = dns_dbiterator_current(gdbiter, &node, name); + check_dns_dbiterator_current(result); + /* + * The origin was handled by signapex(). + */ + if (dns_name_equal(name, gorigin)) { + dns_db_detachnode(gdb, &node); + goto next; + } + /* + * Sort the zone data from the glue and out-of-zone data. + * For NSEC zones nodes with zone data have NSEC records. + * For NSEC3 zones the NSEC3 nodes are zone data but + * outside of the zone name space. For the rest we need + * to track the bottom of zone cuts. + * Nodes which don't need to be signed are dumped here. + */ + dns_rdataset_init(&nsec); + result = dns_db_findrdataset(gdb, node, gversion, nsec_datatype, + 0, 0, &nsec, NULL); + if (dns_rdataset_isassociated(&nsec)) { + dns_rdataset_disassociate(&nsec); + } + if (result == ISC_R_SUCCESS) { + found = true; + } else if (nsec_datatype == dns_rdatatype_nsec3) { + if (dns_name_issubdomain(name, gorigin) && + (zonecut == NULL || + !dns_name_issubdomain(name, zonecut))) + { + if (is_delegation(gdb, gversion, gorigin, name, + node, NULL)) + { + zonecut = savezonecut(&fzonecut, name); + if (!OPTOUT(nsec3flags) || + secure(name, node)) + { + found = true; + } + } else if (has_dname(gdb, gversion, node)) { + zonecut = savezonecut(&fzonecut, name); + found = true; + } else { + found = true; + } + } + } + + if (!found) { + dumpnode(name, node); + dns_db_detachnode(gdb, &node); + } + + next: + result = dns_dbiterator_next(gdbiter); + if (result == ISC_R_NOMORE) { + atomic_store(&finished, true); + break; + } else if (result != ISC_R_SUCCESS) { + fatal("failure iterating database: %s", + isc_result_totext(result)); + } + } + if (!found) { + ended++; + if (ended == ntasks) { + isc_task_detach(&task); + isc_app_shutdown(); + } + isc_mem_put(mctx, fname, sizeof(dns_fixedname_t)); + goto unlock; + } + sevent = (sevent_t *)isc_event_allocate(mctx, task, SIGNER_EVENT_WORK, + sign, NULL, sizeof(sevent_t)); + + sevent->node = node; + sevent->fname = fname; + isc_task_send(worker, ISC_EVENT_PTR(&sevent)); +unlock: + UNLOCK(&namelock); +} + +/*% + * Start a worker task + */ +static void +startworker(isc_task_t *task, isc_event_t *event) { + isc_task_t *worker; + + worker = (isc_task_t *)event->ev_arg; + assignwork(task, worker); + isc_event_free(&event); +} + +/*% + * Write a node to the output file, and restart the worker task. + */ +static void +writenode(isc_task_t *task, isc_event_t *event) { + isc_task_t *worker; + sevent_t *sevent = (sevent_t *)event; + + worker = (isc_task_t *)event->ev_sender; + dumpnode(dns_fixedname_name(sevent->fname), sevent->node); + cleannode(gdb, gversion, sevent->node); + dns_db_detachnode(gdb, &sevent->node); + isc_mem_put(mctx, sevent->fname, sizeof(dns_fixedname_t)); + assignwork(task, worker); + isc_event_free(&event); +} + +/*% + * Sign a database node. + */ +static void +sign(isc_task_t *task, isc_event_t *event) { + dns_fixedname_t *fname; + dns_dbnode_t *node; + sevent_t *sevent, *wevent; + + sevent = (sevent_t *)event; + node = sevent->node; + fname = sevent->fname; + isc_event_free(&event); + + signname(node, dns_fixedname_name(fname)); + wevent = (sevent_t *)isc_event_allocate(mctx, task, SIGNER_EVENT_WRITE, + writenode, NULL, + sizeof(sevent_t)); + wevent->node = node; + wevent->fname = fname; + isc_task_send(main_task, ISC_EVENT_PTR(&wevent)); +} + +/*% + * Update / remove the DS RRset. Preserve RRSIG(DS) if possible. + */ +static void +add_ds(dns_name_t *name, dns_dbnode_t *node, uint32_t nsttl) { + dns_rdataset_t dsset; + dns_rdataset_t sigdsset; + isc_result_t result; + + dns_rdataset_init(&dsset); + dns_rdataset_init(&sigdsset); + result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_ds, 0, + 0, &dsset, &sigdsset); + if (result == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&dsset); + result = dns_db_deleterdataset(gdb, node, gversion, + dns_rdatatype_ds, 0); + check_result(result, "dns_db_deleterdataset"); + } + + result = loadds(name, nsttl, &dsset); + if (result == ISC_R_SUCCESS) { + result = dns_db_addrdataset(gdb, node, gversion, 0, &dsset, 0, + NULL); + check_result(result, "dns_db_addrdataset"); + dns_rdataset_disassociate(&dsset); + if (dns_rdataset_isassociated(&sigdsset)) { + dns_rdataset_disassociate(&sigdsset); + } + } else if (dns_rdataset_isassociated(&sigdsset)) { + result = dns_db_deleterdataset(gdb, node, gversion, + dns_rdatatype_rrsig, + dns_rdatatype_ds); + check_result(result, "dns_db_deleterdataset"); + dns_rdataset_disassociate(&sigdsset); + } +} + +/* + * Remove records of the given type and their signatures. + */ +static void +remove_records(dns_dbnode_t *node, dns_rdatatype_t which, bool checknsec) { + isc_result_t result; + dns_rdatatype_t type, covers; + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdataset_t rdataset; + + dns_rdataset_init(&rdataset); + + /* + * Delete any records of the given type at the apex. + */ + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + type = rdataset.type; + covers = rdataset.covers; + dns_rdataset_disassociate(&rdataset); + if (type == which || covers == which) { + if (which == dns_rdatatype_nsec && checknsec && + !update_chain) + { + fatal("Zone contains NSEC records. Use -u " + "to update to NSEC3."); + } + if (which == dns_rdatatype_nsec3param && checknsec && + !update_chain) + { + fatal("Zone contains NSEC3 chains. Use -u " + "to update to NSEC."); + } + result = dns_db_deleterdataset(gdb, node, gversion, + type, covers); + check_result(result, "dns_db_deleterdataset()"); + } + } + dns_rdatasetiter_destroy(&rdsiter); +} + +/* + * Remove signatures covering the given type. If type == 0, + * then remove all signatures, unless this is a delegation, in + * which case remove all signatures except for DS or nsec_datatype + */ +static void +remove_sigs(dns_dbnode_t *node, bool delegation, dns_rdatatype_t which) { + isc_result_t result; + dns_rdatatype_t type, covers; + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdataset_t rdataset; + + dns_rdataset_init(&rdataset); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + type = rdataset.type; + covers = rdataset.covers; + dns_rdataset_disassociate(&rdataset); + + if (type != dns_rdatatype_rrsig) { + continue; + } + + if (which == 0 && delegation && + (dns_rdatatype_atparent(covers) || + (nsec_datatype == dns_rdatatype_nsec && + covers == nsec_datatype))) + { + continue; + } + + if (which != 0 && covers != which) { + continue; + } + + result = dns_db_deleterdataset(gdb, node, gversion, type, + covers); + check_result(result, "dns_db_deleterdataset()"); + } + dns_rdatasetiter_destroy(&rdsiter); +} + +/*% + * Generate NSEC records for the zone and remove NSEC3/NSEC3PARAM records. + */ +static void +nsecify(void) { + dns_dbiterator_t *dbiter = NULL; + dns_dbnode_t *node = NULL, *nextnode = NULL; + dns_fixedname_t fname, fnextname, fzonecut; + dns_name_t *name, *nextname, *zonecut; + dns_rdataset_t rdataset; + dns_rdatasetiter_t *rdsiter = NULL; + dns_rdatatype_t type, covers; + bool done = false; + isc_result_t result; + uint32_t nsttl = 0; + + dns_rdataset_init(&rdataset); + name = dns_fixedname_initname(&fname); + nextname = dns_fixedname_initname(&fnextname); + zonecut = NULL; + + /* + * Remove any NSEC3 chains. + */ + result = dns_db_createiterator(gdb, DNS_DB_NSEC3ONLY, &dbiter); + check_result(result, "dns_db_createiterator()"); + for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter)) + { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, + &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + type = rdataset.type; + covers = rdataset.covers; + dns_rdataset_disassociate(&rdataset); + result = dns_db_deleterdataset(gdb, node, gversion, + type, covers); + check_result(result, "dns_db_deleterdataset(nsec3param/" + "rrsig)"); + } + dns_rdatasetiter_destroy(&rdsiter); + dns_db_detachnode(gdb, &node); + } + dns_dbiterator_destroy(&dbiter); + + result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter); + check_result(result, "dns_db_createiterator()"); + + result = dns_dbiterator_first(dbiter); + check_result(result, "dns_dbiterator_first()"); + + while (!done) { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + /* + * Skip out-of-zone records. + */ + if (!dns_name_issubdomain(name, gorigin)) { + result = dns_dbiterator_next(dbiter); + if (result == ISC_R_NOMORE) { + done = true; + } else { + check_result(result, "dns_dbiterator_next()"); + } + dns_db_detachnode(gdb, &node); + continue; + } + + if (dns_name_equal(name, gorigin)) { + remove_records(node, dns_rdatatype_nsec3param, true); + /* Clean old rrsigs at apex. */ + (void)active_node(node); + } + + if (is_delegation(gdb, gversion, gorigin, name, node, &nsttl)) { + zonecut = savezonecut(&fzonecut, name); + remove_sigs(node, true, 0); + if (generateds) { + add_ds(name, node, nsttl); + } + } else if (has_dname(gdb, gversion, node)) { + zonecut = savezonecut(&fzonecut, name); + } + + result = dns_dbiterator_next(dbiter); + nextnode = NULL; + while (result == ISC_R_SUCCESS) { + bool active = false; + result = dns_dbiterator_current(dbiter, &nextnode, + nextname); + check_dns_dbiterator_current(result); + active = active_node(nextnode); + if (!active) { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (!dns_name_issubdomain(nextname, gorigin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + remove_sigs(nextnode, false, 0); + remove_records(nextnode, dns_rdatatype_nsec, + false); + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + dns_db_detachnode(gdb, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + dns_name_clone(gorigin, nextname); + done = true; + } else if (result != ISC_R_SUCCESS) { + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + } + dns_dbiterator_pause(dbiter); + result = dns_nsec_build(gdb, gversion, node, nextname, + zone_soa_min_ttl); + check_result(result, "dns_nsec_build()"); + dns_db_detachnode(gdb, &node); + } + + dns_dbiterator_destroy(&dbiter); +} + +static void +addnsec3param(const unsigned char *salt, size_t salt_len, + dns_iterations_t iterations) { + dns_dbnode_t *node = NULL; + dns_rdata_nsec3param_t nsec3param; + unsigned char nsec3parambuf[5 + 255]; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_buffer_t b; + isc_result_t result; + + dns_rdataset_init(&rdataset); + + nsec3param.common.rdclass = gclass; + nsec3param.common.rdtype = dns_rdatatype_nsec3param; + ISC_LINK_INIT(&nsec3param.common, link); + nsec3param.mctx = NULL; + nsec3param.flags = 0; + nsec3param.hash = unknownalg ? DNS_NSEC3_UNKNOWNALG : dns_hash_sha1; + nsec3param.iterations = iterations; + nsec3param.salt_length = (unsigned char)salt_len; + DE_CONST(salt, nsec3param.salt); + + isc_buffer_init(&b, nsec3parambuf, sizeof(nsec3parambuf)); + result = dns_rdata_fromstruct(&rdata, gclass, dns_rdatatype_nsec3param, + &nsec3param, &b); + check_result(result, "dns_rdata_fromstruct()"); + dns_rdatalist_init(&rdatalist); + rdatalist.rdclass = rdata.rdclass; + rdatalist.type = rdata.type; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + result = dns_rdatalist_tordataset(&rdatalist, &rdataset); + check_result(result, "dns_rdatalist_tordataset()"); + + result = dns_db_findnode(gdb, gorigin, true, &node); + check_result(result, "dns_db_findnode(gorigin)"); + + /* + * Delete any current NSEC3PARAM records. + */ + result = dns_db_deleterdataset(gdb, node, gversion, + dns_rdatatype_nsec3param, 0); + if (result == DNS_R_UNCHANGED) { + result = ISC_R_SUCCESS; + } + check_result(result, "dddnsec3param: dns_db_deleterdataset()"); + + result = dns_db_addrdataset(gdb, node, gversion, 0, &rdataset, + DNS_DBADD_MERGE, NULL); + if (result == DNS_R_UNCHANGED) { + result = ISC_R_SUCCESS; + } + check_result(result, "addnsec3param: dns_db_addrdataset()"); + dns_db_detachnode(gdb, &node); +} + +static void +addnsec3(dns_name_t *name, dns_dbnode_t *node, const unsigned char *salt, + size_t salt_len, unsigned int iterations, hashlist_t *hashlist, + dns_ttl_t ttl) { + unsigned char hash[NSEC3_MAX_HASH_LENGTH]; + const unsigned char *nexthash; + unsigned char nsec3buffer[DNS_NSEC3_BUFFERSIZE]; + dns_fixedname_t hashname; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + dns_dbnode_t *nsec3node = NULL; + char namebuf[DNS_NAME_FORMATSIZE]; + size_t hash_len; + + dns_name_format(name, namebuf, sizeof(namebuf)); + + dns_fixedname_init(&hashname); + dns_rdataset_init(&rdataset); + + dns_name_downcase(name, name, NULL); + result = dns_nsec3_hashname(&hashname, hash, &hash_len, name, gorigin, + dns_hash_sha1, iterations, salt, salt_len); + check_result(result, "addnsec3: dns_nsec3_hashname()"); + nexthash = hashlist_findnext(hashlist, hash); + result = dns_nsec3_buildrdata( + gdb, gversion, node, + unknownalg ? DNS_NSEC3_UNKNOWNALG : dns_hash_sha1, nsec3flags, + iterations, salt, salt_len, nexthash, ISC_SHA1_DIGESTLENGTH, + nsec3buffer, &rdata); + check_result(result, "addnsec3: dns_nsec3_buildrdata()"); + dns_rdatalist_init(&rdatalist); + rdatalist.rdclass = rdata.rdclass; + rdatalist.type = rdata.type; + rdatalist.ttl = ttl; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + result = dns_rdatalist_tordataset(&rdatalist, &rdataset); + check_result(result, "dns_rdatalist_tordataset()"); + result = dns_db_findnsec3node(gdb, dns_fixedname_name(&hashname), true, + &nsec3node); + check_result(result, "addnsec3: dns_db_findnode()"); + result = dns_db_addrdataset(gdb, nsec3node, gversion, 0, &rdataset, 0, + NULL); + if (result == DNS_R_UNCHANGED) { + result = ISC_R_SUCCESS; + } + check_result(result, "addnsec3: dns_db_addrdataset()"); + dns_db_detachnode(gdb, &nsec3node); +} + +/*% + * Clean out NSEC3 record and RRSIG(NSEC3) that are not in the hash list. + * + * Extract the hash from the first label of 'name' then see if it + * is in hashlist. If 'name' is not in the hashlist then delete the + * any NSEC3 records which have the same parameters as the chain we + * are building. + * + * XXXMPA Should we also check that it of the form <hash>.<origin>? + */ +static void +nsec3clean(dns_name_t *name, dns_dbnode_t *node, unsigned int hashalg, + unsigned int iterations, const unsigned char *salt, size_t salt_len, + hashlist_t *hashlist) { + dns_label_t label; + dns_rdata_nsec3_t nsec3; + dns_rdata_t rdata, delrdata; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset, delrdataset; + bool delete_rrsigs = false; + isc_buffer_t target; + isc_result_t result; + unsigned char hash[NSEC3_MAX_HASH_LENGTH + 1]; + bool exists; + + /* + * Get the first label. + */ + dns_name_getlabel(name, 0, &label); + + /* + * We want just the label contents. + */ + isc_region_consume(&label, 1); + + /* + * Decode base32hex string. + */ + isc_buffer_init(&target, hash, sizeof(hash) - 1); + result = isc_base32hex_decoderegion(&label, &target); + if (result != ISC_R_SUCCESS) { + return; + } + + hash[isc_buffer_usedlength(&target)] = 0; + + exists = hashlist_exists(hashlist, hash); + + /* + * Verify that the NSEC3 parameters match the current ones + * otherwise we are dealing with a different NSEC3 chain. + */ + dns_rdataset_init(&rdataset); + dns_rdataset_init(&delrdataset); + + result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_nsec3, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + return; + } + + /* + * Delete any NSEC3 records which are not part of the current + * NSEC3 chain. + */ + for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(&rdataset)) + { + dns_rdata_init(&rdata); + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct"); + if (exists && nsec3.hash == hashalg && + nsec3.iterations == iterations && + nsec3.salt_length == salt_len && + isc_safe_memequal(nsec3.salt, salt, salt_len)) + { + continue; + } + dns_rdatalist_init(&rdatalist); + rdatalist.rdclass = rdata.rdclass; + rdatalist.type = rdata.type; + if (set_maxttl) { + rdatalist.ttl = ISC_MIN(rdataset.ttl, maxttl); + } + dns_rdata_init(&delrdata); + dns_rdata_clone(&rdata, &delrdata); + ISC_LIST_APPEND(rdatalist.rdata, &delrdata, link); + result = dns_rdatalist_tordataset(&rdatalist, &delrdataset); + check_result(result, "dns_rdatalist_tordataset()"); + result = dns_db_subtractrdataset(gdb, node, gversion, + &delrdataset, 0, NULL); + dns_rdataset_disassociate(&delrdataset); + if (result != ISC_R_SUCCESS && result != DNS_R_NXRRSET) { + check_result(result, "dns_db_subtractrdataset(NSEC3)"); + } + delete_rrsigs = true; + } + dns_rdataset_disassociate(&rdataset); + if (result != ISC_R_NOMORE) { + check_result(result, "dns_rdataset_first/next"); + } + + if (!delete_rrsigs) { + return; + } + /* + * Delete the NSEC3 RRSIGs + */ + result = dns_db_deleterdataset(gdb, node, gversion, dns_rdatatype_rrsig, + dns_rdatatype_nsec3); + if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) { + check_result(result, "dns_db_deleterdataset(RRSIG(NSEC3))"); + } +} + +static void +rrset_cleanup(dns_name_t *name, dns_rdataset_t *rdataset, dns_diff_t *add, + dns_diff_t *del) { + isc_result_t result; + unsigned int count1 = 0; + dns_rdataset_t tmprdataset; + char namestr[DNS_NAME_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; + + dns_name_format(name, namestr, sizeof(namestr)); + dns_rdatatype_format(rdataset->type, typestr, sizeof(typestr)); + + dns_rdataset_init(&tmprdataset); + for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + dns_rdata_t rdata1 = DNS_RDATA_INIT; + unsigned int count2 = 0; + + count1++; + dns_rdataset_current(rdataset, &rdata1); + dns_rdataset_clone(rdataset, &tmprdataset); + for (result = dns_rdataset_first(&tmprdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&tmprdataset)) + { + dns_rdata_t rdata2 = DNS_RDATA_INIT; + dns_difftuple_t *tuple = NULL; + count2++; + dns_rdataset_current(&tmprdataset, &rdata2); + if (count1 < count2 && + dns_rdata_casecompare(&rdata1, &rdata2) == 0) + { + vbprintf(2, "removing duplicate at %s/%s\n", + namestr, typestr); + result = dns_difftuple_create( + mctx, DNS_DIFFOP_DELRESIGN, name, + rdataset->ttl, &rdata2, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(del, &tuple); + } else if (set_maxttl && rdataset->ttl > maxttl) { + vbprintf(2, + "reducing ttl of %s/%s " + "from %d to %d\n", + namestr, typestr, rdataset->ttl, + maxttl); + result = dns_difftuple_create( + mctx, DNS_DIFFOP_DELRESIGN, name, + rdataset->ttl, &rdata2, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(del, &tuple); + tuple = NULL; + result = dns_difftuple_create( + mctx, DNS_DIFFOP_ADDRESIGN, name, + maxttl, &rdata2, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(add, &tuple); + } + } + dns_rdataset_disassociate(&tmprdataset); + } +} + +static void +cleanup_zone(void) { + isc_result_t result; + dns_dbiterator_t *dbiter = NULL; + dns_rdatasetiter_t *rdsiter = NULL; + dns_diff_t add, del; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_fixedname_t fname; + dns_name_t *name; + + dns_diff_init(mctx, &add); + dns_diff_init(mctx, &del); + name = dns_fixedname_initname(&fname); + dns_rdataset_init(&rdataset); + + result = dns_db_createiterator(gdb, 0, &dbiter); + check_result(result, "dns_db_createiterator()"); + + for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter)) + { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + result = dns_db_allrdatasets(gdb, node, gversion, 0, 0, + &rdsiter); + check_result(result, "dns_db_allrdatasets()"); + for (result = dns_rdatasetiter_first(rdsiter); + result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(rdsiter)) + { + dns_rdatasetiter_current(rdsiter, &rdataset); + rrset_cleanup(name, &rdataset, &add, &del); + dns_rdataset_disassociate(&rdataset); + } + if (result != ISC_R_NOMORE) { + fatal("rdatasets iteration failed."); + } + dns_rdatasetiter_destroy(&rdsiter); + dns_db_detachnode(gdb, &node); + } + if (result != ISC_R_NOMORE) { + fatal("zone iteration failed."); + } + + result = dns_diff_applysilently(&del, gdb, gversion); + check_result(result, "dns_diff_applysilently"); + + result = dns_diff_applysilently(&add, gdb, gversion); + check_result(result, "dns_diff_applysilently"); + + dns_diff_clear(&del); + dns_diff_clear(&add); + dns_dbiterator_destroy(&dbiter); +} + +/* + * Generate NSEC3 records for the zone. + */ +static void +nsec3ify(unsigned int hashalg, dns_iterations_t iterations, + const unsigned char *salt, size_t salt_len, hashlist_t *hashlist) { + dns_dbiterator_t *dbiter = NULL; + dns_dbnode_t *node = NULL, *nextnode = NULL; + dns_fixedname_t fname, fnextname, fzonecut; + dns_name_t *name, *nextname, *zonecut; + dns_rdataset_t rdataset; + int order; + bool active; + bool done = false; + isc_result_t result; + uint32_t nsttl = 0; + unsigned int count, nlabels; + + dns_rdataset_init(&rdataset); + name = dns_fixedname_initname(&fname); + nextname = dns_fixedname_initname(&fnextname); + zonecut = NULL; + + /* + * Walk the zone generating the hash names. + */ + result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter); + check_result(result, "dns_db_createiterator()"); + + result = dns_dbiterator_first(dbiter); + check_result(result, "dns_dbiterator_first()"); + + while (!done) { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + /* + * Skip out-of-zone records. + */ + if (!dns_name_issubdomain(name, gorigin)) { + result = dns_dbiterator_next(dbiter); + if (result == ISC_R_NOMORE) { + done = true; + } else { + check_result(result, "dns_dbiterator_next()"); + } + dns_db_detachnode(gdb, &node); + continue; + } + + if (dns_name_equal(name, gorigin)) { + remove_records(node, dns_rdatatype_nsec, true); + /* Clean old rrsigs at apex. */ + (void)active_node(node); + } + + if (has_dname(gdb, gversion, node)) { + zonecut = savezonecut(&fzonecut, name); + } + + result = dns_dbiterator_next(dbiter); + nextnode = NULL; + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiter, &nextnode, + nextname); + check_dns_dbiterator_current(result); + active = active_node(nextnode); + if (!active) { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (!dns_name_issubdomain(nextname, gorigin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + remove_sigs(nextnode, false, 0); + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (is_delegation(gdb, gversion, gorigin, nextname, + nextnode, &nsttl)) + { + zonecut = savezonecut(&fzonecut, nextname); + remove_sigs(nextnode, true, 0); + if (generateds) { + add_ds(nextname, nextnode, nsttl); + } + if (OPTOUT(nsec3flags) && + !secure(nextname, nextnode)) + { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + } else if (has_dname(gdb, gversion, nextnode)) { + zonecut = savezonecut(&fzonecut, nextname); + } + dns_db_detachnode(gdb, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + dns_name_copy(gorigin, nextname); + done = true; + } else if (result != ISC_R_SUCCESS) { + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + } + dns_name_downcase(name, name, NULL); + hashlist_add_dns_name(hashlist, name, hashalg, iterations, salt, + salt_len, false); + dns_db_detachnode(gdb, &node); + /* + * Add hashes for empty nodes. Use closest encloser logic. + * The closest encloser either has data or is a empty + * node for another span so we don't add + * it here. Empty labels on nextname are within the span. + */ + dns_name_downcase(nextname, nextname, NULL); + dns_name_fullcompare(name, nextname, &order, &nlabels); + addnowildcardhash(hashlist, name, hashalg, iterations, salt, + salt_len); + count = dns_name_countlabels(nextname); + while (count > nlabels + 1) { + count--; + dns_name_split(nextname, count, NULL, nextname); + hashlist_add_dns_name(hashlist, nextname, hashalg, + iterations, salt, salt_len, + false); + addnowildcardhash(hashlist, nextname, hashalg, + iterations, salt, salt_len); + } + } + dns_dbiterator_destroy(&dbiter); + + /* + * We have all the hashes now so we can sort them. + */ + hashlist_sort(hashlist); + + /* + * Check for duplicate hashes. If found the salt needs to + * be changed. + */ + if (hashlist_hasdup(hashlist)) { + fatal("Duplicate hash detected. Pick a different salt."); + } + + /* + * Generate the nsec3 records. + */ + zonecut = NULL; + done = false; + + addnsec3param(salt, salt_len, iterations); + + /* + * Clean out NSEC3 records which don't match this chain. + */ + result = dns_db_createiterator(gdb, DNS_DB_NSEC3ONLY, &dbiter); + check_result(result, "dns_db_createiterator()"); + + for (result = dns_dbiterator_first(dbiter); result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter)) + { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + nsec3clean(name, node, hashalg, iterations, salt, salt_len, + hashlist); + dns_db_detachnode(gdb, &node); + } + dns_dbiterator_destroy(&dbiter); + + /* + * Generate / complete the new chain. + */ + result = dns_db_createiterator(gdb, DNS_DB_NONSEC3, &dbiter); + check_result(result, "dns_db_createiterator()"); + + result = dns_dbiterator_first(dbiter); + check_result(result, "dns_dbiterator_first()"); + + while (!done) { + result = dns_dbiterator_current(dbiter, &node, name); + check_dns_dbiterator_current(result); + /* + * Skip out-of-zone records. + */ + if (!dns_name_issubdomain(name, gorigin)) { + result = dns_dbiterator_next(dbiter); + if (result == ISC_R_NOMORE) { + done = true; + } else { + check_result(result, "dns_dbiterator_next()"); + } + dns_db_detachnode(gdb, &node); + continue; + } + + if (has_dname(gdb, gversion, node)) { + zonecut = savezonecut(&fzonecut, name); + } + + result = dns_dbiterator_next(dbiter); + nextnode = NULL; + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiter, &nextnode, + nextname); + check_dns_dbiterator_current(result); + active = active_node(nextnode); + if (!active) { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (!dns_name_issubdomain(nextname, gorigin) || + (zonecut != NULL && + dns_name_issubdomain(nextname, zonecut))) + { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + if (is_delegation(gdb, gversion, gorigin, nextname, + nextnode, NULL)) + { + zonecut = savezonecut(&fzonecut, nextname); + if (OPTOUT(nsec3flags) && + !secure(nextname, nextnode)) + { + dns_db_detachnode(gdb, &nextnode); + result = dns_dbiterator_next(dbiter); + continue; + } + } else if (has_dname(gdb, gversion, nextnode)) { + zonecut = savezonecut(&fzonecut, nextname); + } + dns_db_detachnode(gdb, &nextnode); + break; + } + if (result == ISC_R_NOMORE) { + dns_name_copy(gorigin, nextname); + done = true; + } else if (result != ISC_R_SUCCESS) { + fatal("iterating through the database failed: %s", + isc_result_totext(result)); + } + /* + * We need to pause here to release the lock on the database. + */ + dns_dbiterator_pause(dbiter); + addnsec3(name, node, salt, salt_len, iterations, hashlist, + zone_soa_min_ttl); + dns_db_detachnode(gdb, &node); + /* + * Add NSEC3's for empty nodes. Use closest encloser logic. + */ + dns_name_fullcompare(name, nextname, &order, &nlabels); + count = dns_name_countlabels(nextname); + while (count > nlabels + 1) { + count--; + dns_name_split(nextname, count, NULL, nextname); + addnsec3(nextname, NULL, salt, salt_len, iterations, + hashlist, zone_soa_min_ttl); + } + } + dns_dbiterator_destroy(&dbiter); +} + +/*% + * Load the zone file from disk + */ +static void +loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) { + isc_buffer_t b; + int len; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result; + + len = strlen(origin); + isc_buffer_init(&b, origin, len); + isc_buffer_add(&b, len); + + name = dns_fixedname_initname(&fname); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fatal("failed converting name '%s' to dns format: %s", origin, + isc_result_totext(result)); + } + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, db); + check_result(result, "dns_db_create()"); + + result = dns_db_load(*db, file, inputformat, 0); + if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) { + fatal("failed loading zone from '%s': %s", file, + isc_result_totext(result)); + } +} + +/*% + * Finds all public zone keys in the zone, and attempts to load the + * private keys from disk. + */ +static void +loadzonekeys(bool preserve_keys, bool load_public) { + dns_dbnode_t *node; + dns_dbversion_t *currentversion = NULL; + isc_result_t result; + dns_rdataset_t rdataset, keysigs, soasigs; + + node = NULL; + result = dns_db_findnode(gdb, gorigin, false, &node); + if (result != ISC_R_SUCCESS) { + fatal("failed to find the zone's origin: %s", + isc_result_totext(result)); + } + + dns_db_currentversion(gdb, ¤tversion); + + dns_rdataset_init(&rdataset); + dns_rdataset_init(&soasigs); + dns_rdataset_init(&keysigs); + + /* Make note of the keys which signed the SOA, if any */ + result = dns_db_findrdataset(gdb, node, currentversion, + dns_rdatatype_soa, 0, 0, &rdataset, + &soasigs); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* Preserve the TTL of the DNSKEY RRset, if any */ + dns_rdataset_disassociate(&rdataset); + result = dns_db_findrdataset(gdb, node, currentversion, + dns_rdatatype_dnskey, 0, 0, &rdataset, + &keysigs); + + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (set_keyttl && keyttl != rdataset.ttl) { + fprintf(stderr, + "User-specified TTL %u conflicts " + "with existing DNSKEY RRset TTL.\n", + keyttl); + fprintf(stderr, + "Imported keys will use the RRSet " + "TTL %u instead.\n", + rdataset.ttl); + } + keyttl = rdataset.ttl; + + /* Load keys corresponding to the existing DNSKEY RRset. */ + result = dns_dnssec_keylistfromrdataset( + gorigin, directory, mctx, &rdataset, &keysigs, &soasigs, + preserve_keys, load_public, &keylist); + if (result != ISC_R_SUCCESS) { + fatal("failed to load the zone keys: %s", + isc_result_totext(result)); + } + +cleanup: + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + if (dns_rdataset_isassociated(&keysigs)) { + dns_rdataset_disassociate(&keysigs); + } + if (dns_rdataset_isassociated(&soasigs)) { + dns_rdataset_disassociate(&soasigs); + } + dns_db_detachnode(gdb, &node); + dns_db_closeversion(gdb, ¤tversion, false); +} + +static void +loadexplicitkeys(char *keyfiles[], int n, bool setksk) { + isc_result_t result; + int i; + + for (i = 0; i < n; i++) { + dns_dnsseckey_t *key = NULL; + dst_key_t *newkey = NULL; + + result = dst_key_fromnamedfile( + keyfiles[i], directory, + DST_TYPE_PUBLIC | DST_TYPE_PRIVATE, mctx, &newkey); + if (result != ISC_R_SUCCESS) { + fatal("cannot load dnskey %s: %s", keyfiles[i], + isc_result_totext(result)); + } + + if (!dns_name_equal(gorigin, dst_key_name(newkey))) { + fatal("key %s not at origin\n", keyfiles[i]); + } + + if (!dst_key_isprivate(newkey)) { + fatal("cannot sign zone with non-private dnskey %s", + keyfiles[i]); + } + + /* Skip any duplicates */ + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (dst_key_id(key->key) == dst_key_id(newkey) && + dst_key_alg(key->key) == dst_key_alg(newkey)) + { + break; + } + } + + if (key == NULL) { + /* We haven't seen this key before */ + dns_dnsseckey_create(mctx, &newkey, &key); + ISC_LIST_APPEND(keylist, key, link); + key->source = dns_keysource_user; + } else { + dst_key_free(&key->key); + key->key = newkey; + } + + key->force_publish = true; + key->force_sign = true; + + if (setksk) { + key->ksk = true; + } + } +} + +static void +report(const char *format, ...) { + if (!quiet) { + FILE *out = output_stdout ? stderr : stdout; + char buf[4096]; + va_list args; + + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + fprintf(out, "%s\n", buf); + } +} + +static void +clear_keylist(dns_dnsseckeylist_t *list) { + dns_dnsseckey_t *key; + while (!ISC_LIST_EMPTY(*list)) { + key = ISC_LIST_HEAD(*list); + ISC_LIST_UNLINK(*list, key, link); + dns_dnsseckey_destroy(mctx, &key); + } +} + +static void +build_final_keylist(void) { + isc_result_t result; + dns_dbnode_t *node = NULL; + dns_dbversion_t *ver = NULL; + dns_diff_t diff; + dns_dnsseckeylist_t rmkeys, matchkeys; + char name[DNS_NAME_FORMATSIZE]; + dns_rdataset_t cdsset, cdnskeyset, soaset; + + ISC_LIST_INIT(rmkeys); + ISC_LIST_INIT(matchkeys); + + dns_rdataset_init(&soaset); + dns_rdataset_init(&cdsset); + dns_rdataset_init(&cdnskeyset); + + /* + * Find keys that match this zone in the key repository. + */ + result = dns_dnssec_findmatchingkeys(gorigin, directory, now, mctx, + &matchkeys); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + } + check_result(result, "dns_dnssec_findmatchingkeys"); + + result = dns_db_newversion(gdb, &ver); + check_result(result, "dns_db_newversion"); + + result = dns_db_getoriginnode(gdb, &node); + check_result(result, "dns_db_getoriginnode"); + + /* Get the CDS rdataset */ + result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_cds, + dns_rdatatype_none, 0, &cdsset, NULL); + if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdsset)) { + dns_rdataset_disassociate(&cdsset); + } + + /* Get the CDNSKEY rdataset */ + result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_cdnskey, + dns_rdatatype_none, 0, &cdnskeyset, NULL); + if (result != ISC_R_SUCCESS && dns_rdataset_isassociated(&cdnskeyset)) { + dns_rdataset_disassociate(&cdnskeyset); + } + + dns_diff_init(mctx, &diff); + + /* + * Update keylist with information from from the key repository. + */ + dns_dnssec_updatekeys(&keylist, &matchkeys, NULL, gorigin, keyttl, + &diff, mctx, report); + + /* + * Update keylist with sync records. + */ + dns_dnssec_syncupdate(&keylist, &rmkeys, &cdsset, &cdnskeyset, now, + keyttl, &diff, mctx); + + dns_name_format(gorigin, name, sizeof(name)); + + result = dns_diff_applysilently(&diff, gdb, ver); + if (result != ISC_R_SUCCESS) { + fatal("failed to update DNSKEY RRset at node '%s': %s", name, + isc_result_totext(result)); + } + + dns_db_detachnode(gdb, &node); + dns_db_closeversion(gdb, &ver, true); + + dns_diff_clear(&diff); + + if (dns_rdataset_isassociated(&cdsset)) { + dns_rdataset_disassociate(&cdsset); + } + if (dns_rdataset_isassociated(&cdnskeyset)) { + dns_rdataset_disassociate(&cdnskeyset); + } + + clear_keylist(&rmkeys); + clear_keylist(&matchkeys); +} + +static void +warnifallksk(dns_db_t *db) { + dns_dbversion_t *currentversion = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + dns_rdata_dnskey_t dnskey; + bool have_non_ksk = false; + + dns_db_currentversion(db, ¤tversion); + + result = dns_db_findnode(db, gorigin, false, &node); + if (result != ISC_R_SUCCESS) { + fatal("failed to find the zone's origin: %s", + isc_result_totext(result)); + } + + dns_rdataset_init(&rdataset); + result = dns_db_findrdataset(db, node, currentversion, + dns_rdatatype_dnskey, 0, 0, &rdataset, + NULL); + if (result != ISC_R_SUCCESS) { + fatal("failed to find keys at the zone apex: %s", + isc_result_totext(result)); + } + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first"); + while (result == ISC_R_SUCCESS) { + dns_rdata_reset(&rdata); + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &dnskey, NULL); + check_result(result, "dns_rdata_tostruct"); + if ((dnskey.flags & DNS_KEYFLAG_KSK) == 0) { + have_non_ksk = true; + result = ISC_R_NOMORE; + } else { + result = dns_rdataset_next(&rdataset); + } + dns_rdata_freestruct(&dnskey); + } + dns_rdataset_disassociate(&rdataset); + dns_db_detachnode(db, &node); + dns_db_closeversion(db, ¤tversion, false); + if (!have_non_ksk && !ignore_kskflag) { + if (disable_zone_check) { + fprintf(stderr, + "%s: warning: No non-KSK DNSKEY found; " + "supply a ZSK or use '-z'.\n", + program); + } else { + fatal("No non-KSK DNSKEY found; " + "supply a ZSK or use '-z'."); + } + } +} + +static void +set_nsec3params(bool update, bool set_salt, bool set_optout, bool set_iter) { + isc_result_t result; + dns_dbversion_t *ver = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_nsec3_t nsec3; + dns_fixedname_t fname; + dns_name_t *hashname; + unsigned char orig_salt[255]; + size_t orig_saltlen; + dns_hash_t orig_hash; + uint16_t orig_iter; + + dns_db_currentversion(gdb, &ver); + dns_rdataset_init(&rdataset); + + orig_saltlen = sizeof(orig_salt); + result = dns_db_getnsec3parameters(gdb, ver, &orig_hash, NULL, + &orig_iter, orig_salt, + &orig_saltlen); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + nsec_datatype = dns_rdatatype_nsec3; + + if (!update && set_salt) { + if (salt_length != orig_saltlen || + !isc_safe_memequal(saltbuf, orig_salt, salt_length)) + { + fatal("An NSEC3 chain exists with a different salt. " + "Use -u to update it."); + } + } else if (!set_salt) { + salt_length = orig_saltlen; + memmove(saltbuf, orig_salt, orig_saltlen); + gsalt = saltbuf; + } + + if (!update && set_iter) { + if (nsec3iter != orig_iter) { + fatal("An NSEC3 chain exists with different " + "iterations. Use -u to update it."); + } + } else if (!set_iter) { + nsec3iter = orig_iter; + } + + /* + * Find an NSEC3 record to get the current OPTOUT value. + * (This assumes all NSEC3 records agree.) + */ + + hashname = dns_fixedname_initname(&fname); + result = dns_nsec3_hashname(&fname, NULL, NULL, gorigin, gorigin, + dns_hash_sha1, orig_iter, orig_salt, + orig_saltlen); + check_result(result, "dns_nsec3_hashname"); + + result = dns_db_findnsec3node(gdb, hashname, false, &node); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_db_findrdataset(gdb, node, ver, dns_rdatatype_nsec3, 0, 0, + &rdataset, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_rdataset_first(&rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &nsec3, NULL); + check_result(result, "dns_rdata_tostruct"); + + if (!update && set_optout) { + if (nsec3flags != nsec3.flags) { + fatal("An NSEC3 chain exists with%s OPTOUT. " + "Use -u -%s to %s it.", + OPTOUT(nsec3.flags) ? "" : "out", + OPTOUT(nsec3.flags) ? "AA" : "A", + OPTOUT(nsec3.flags) ? "clear" : "set"); + } + } else if (!set_optout) { + nsec3flags = nsec3.flags; + } + + dns_rdata_freestruct(&nsec3); + +cleanup: + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + if (node != NULL) { + dns_db_detachnode(gdb, &node); + } + dns_db_closeversion(gdb, &ver, false); +} + +static void +writeset(const char *prefix, dns_rdatatype_t type) { + char *filename; + char namestr[DNS_NAME_FORMATSIZE]; + dns_db_t *db = NULL; + dns_dbversion_t *dbversion = NULL; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL; + dns_name_t *name; + dns_rdata_t rdata, ds; + bool have_ksk = false; + bool have_non_ksk = false; + isc_buffer_t b; + isc_buffer_t namebuf; + isc_region_t r; + isc_result_t result; + dns_dnsseckey_t *key, *curr; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + unsigned char keybuf[DST_KEY_MAXSIZE]; + unsigned int filenamelen; + const dns_master_style_t *style = (type == dns_rdatatype_dnskey) + ? masterstyle + : dsstyle; + + isc_buffer_init(&namebuf, namestr, sizeof(namestr)); + result = dns_name_tofilenametext(gorigin, false, &namebuf); + check_result(result, "dns_name_tofilenametext"); + isc_buffer_putuint8(&namebuf, 0); + filenamelen = strlen(prefix) + strlen(namestr) + 1; + if (dsdir != NULL) { + filenamelen += strlen(dsdir) + 1; + } + filename = isc_mem_get(mctx, filenamelen); + if (dsdir != NULL) { + snprintf(filename, filenamelen, "%s/", dsdir); + } else { + filename[0] = 0; + } + strlcat(filename, prefix, filenamelen); + strlcat(filename, namestr, filenamelen); + + dns_diff_init(mctx, &diff); + + name = gorigin; + + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (REVOKE(key->key)) { + continue; + } + if (isksk(key)) { + have_ksk = true; + have_non_ksk = false; + } else { + have_ksk = false; + have_non_ksk = true; + } + for (curr = ISC_LIST_HEAD(keylist); curr != NULL; + curr = ISC_LIST_NEXT(curr, link)) + { + if (dst_key_alg(key->key) != dst_key_alg(curr->key)) { + continue; + } + if (REVOKE(curr->key)) { + continue; + } + if (isksk(curr)) { + have_ksk = true; + } else { + have_non_ksk = true; + } + } + if (have_ksk && have_non_ksk && !isksk(key)) { + continue; + } + dns_rdata_init(&rdata); + dns_rdata_init(&ds); + isc_buffer_init(&b, keybuf, sizeof(keybuf)); + result = dst_key_todns(key->key, &b); + check_result(result, "dst_key_todns"); + isc_buffer_usedregion(&b, &r); + dns_rdata_fromregion(&rdata, gclass, dns_rdatatype_dnskey, &r); + if (type != dns_rdatatype_dnskey) { + result = dns_ds_buildrdata(gorigin, &rdata, + DNS_DSDIGEST_SHA256, dsbuf, + &ds); + check_result(result, "dns_ds_buildrdata"); + result = dns_difftuple_create(mctx, + DNS_DIFFOP_ADDRESIGN, + name, 0, &ds, &tuple); + } else { + result = dns_difftuple_create( + mctx, DNS_DIFFOP_ADDRESIGN, gorigin, + zone_soa_min_ttl, &rdata, &tuple); + } + check_result(result, "dns_difftuple_create"); + dns_diff_append(&diff, &tuple); + } + + result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone, + gclass, 0, NULL, &db); + check_result(result, "dns_db_create"); + + result = dns_db_newversion(db, &dbversion); + check_result(result, "dns_db_newversion"); + + result = dns_diff_apply(&diff, db, dbversion); + check_result(result, "dns_diff_apply"); + dns_diff_clear(&diff); + + result = dns_master_dump(mctx, db, dbversion, style, filename, + dns_masterformat_text, NULL); + check_result(result, "dns_master_dump"); + + isc_mem_put(mctx, filename, filenamelen); + + dns_db_closeversion(db, &dbversion, false); + dns_db_detach(&db); +} + +static void +print_time(FILE *fp) { + time_t currenttime = time(NULL); + struct tm t, *tm = localtime_r(¤ttime, &t); + unsigned int flen; + char timebuf[80]; + + if (tm == NULL || outputformat != dns_masterformat_text) { + return; + } + + flen = strftime(timebuf, sizeof(timebuf), "%a %b %e %H:%M:%S %Y", tm); + INSIST(flen > 0U && flen < sizeof(timebuf)); + fprintf(fp, "; File written on %s\n", timebuf); +} + +static void +print_version(FILE *fp) { + if (outputformat != dns_masterformat_text) { + return; + } + + fprintf(fp, "; dnssec_signzone version %s\n", PACKAGE_VERSION); +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t%s [options] zonefile [keys]\n", program); + + fprintf(stderr, "\n"); + + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + + fprintf(stderr, "Options: (default value in parenthesis) \n"); + fprintf(stderr, "\t-S:\tsmart signing: automatically finds key files\n" + "\t\tfor the zone and determines how they are to " + "be used\n"); + fprintf(stderr, "\t-K directory:\n"); + fprintf(stderr, "\t\tdirectory to find key files (.)\n"); + fprintf(stderr, "\t-d directory:\n"); + fprintf(stderr, "\t\tdirectory to find dsset-* files (.)\n"); + fprintf(stderr, "\t-g:\t"); + fprintf(stderr, "update DS records based on child zones' " + "dsset-* files\n"); + fprintf(stderr, "\t-s [YYYYMMDDHHMMSS|+offset]:\n"); + fprintf(stderr, "\t\tRRSIG start time " + "- absolute|offset (now - 1 hour)\n"); + fprintf(stderr, "\t-e [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n"); + fprintf(stderr, "\t\tRRSIG end time " + "- absolute|from start|from now " + "(now + 30 days)\n"); + fprintf(stderr, "\t-X [YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n"); + fprintf(stderr, "\t\tDNSKEY RRSIG end " + "- absolute|from start|from now " + "(matches -e)\n"); + fprintf(stderr, "\t-i interval:\n"); + fprintf(stderr, "\t\tcycle interval - resign " + "if < interval from end ( (end-start)/4 )\n"); + fprintf(stderr, "\t-j jitter:\n"); + fprintf(stderr, "\t\trandomize signature end time up to jitter " + "seconds\n"); + fprintf(stderr, "\t-v debuglevel (0)\n"); + fprintf(stderr, "\t-q quiet\n"); + fprintf(stderr, "\t-V:\tprint version information\n"); + fprintf(stderr, "\t-o origin:\n"); + fprintf(stderr, "\t\tzone origin (name of zonefile)\n"); + fprintf(stderr, "\t-f outfile:\n"); + fprintf(stderr, "\t\tfile the signed zone is written in " + "(zonefile + .signed)\n"); + fprintf(stderr, "\t-I format:\n"); + fprintf(stderr, "\t\tfile format of input zonefile (text)\n"); + fprintf(stderr, "\t-O format:\n"); + fprintf(stderr, "\t\tfile format of signed zone file (text)\n"); + fprintf(stderr, "\t-N format:\n"); + fprintf(stderr, "\t\tsoa serial format of signed zone file (keep)\n"); + fprintf(stderr, "\t-D:\n"); + fprintf(stderr, "\t\toutput only DNSSEC-related records\n"); + fprintf(stderr, "\t-a:\t"); + fprintf(stderr, "verify generated signatures\n"); + fprintf(stderr, "\t-c class (IN)\n"); + fprintf(stderr, "\t-E engine:\n"); + fprintf(stderr, "\t\tname of an OpenSSL engine to use\n"); + fprintf(stderr, "\t-P:\t"); + fprintf(stderr, "disable post-sign verification\n"); + fprintf(stderr, "\t-Q:\t"); + fprintf(stderr, "remove signatures from keys that are no " + "longer active\n"); + fprintf(stderr, "\t-R:\t"); + fprintf(stderr, "remove signatures from keys that no longer exist\n"); + fprintf(stderr, "\t-T TTL:\tTTL for newly added DNSKEYs\n"); + fprintf(stderr, "\t-t:\t"); + fprintf(stderr, "print statistics\n"); + fprintf(stderr, "\t-u:\t"); + fprintf(stderr, "update or replace an existing NSEC/NSEC3 chain\n"); + fprintf(stderr, "\t-x:\tsign DNSKEY record with KSKs only, not ZSKs\n"); + fprintf(stderr, "\t-z:\tsign all records with KSKs\n"); + fprintf(stderr, "\t-C:\tgenerate a keyset file, for compatibility\n" + "\t\twith older versions of dnssec-signzone -g\n"); + fprintf(stderr, "\t-n ncpus (number of cpus present)\n"); + fprintf(stderr, "\t-k key_signing_key\n"); + fprintf(stderr, "\t-3 NSEC3 salt\n"); + fprintf(stderr, "\t-H NSEC3 iterations (10)\n"); + fprintf(stderr, "\t-A NSEC3 optout\n"); + + fprintf(stderr, "\n"); + + fprintf(stderr, "Signing Keys: "); + fprintf(stderr, "(default: all zone keys that have private keys)\n"); + fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n"); + + exit(0); +} + +static void +removetempfile(void) { + if (removefile) { + isc_file_remove(tempfile); + } +} + +static void +print_stats(isc_time_t *timer_start, isc_time_t *timer_finish, + isc_time_t *sign_start, isc_time_t *sign_finish) { + uint64_t time_us; /* Time in microseconds */ + uint64_t time_ms; /* Time in milliseconds */ + uint64_t sig_ms; /* Signatures per millisecond */ + FILE *out = output_stdout ? stderr : stdout; + + fprintf(out, "Signatures generated: %10u\n", nsigned); + fprintf(out, "Signatures retained: %10u\n", nretained); + fprintf(out, "Signatures dropped: %10u\n", ndropped); + fprintf(out, "Signatures successfully verified: %10u\n", nverified); + fprintf(out, + "Signatures unsuccessfully " + "verified: %10u\n", + nverifyfailed); + + time_us = isc_time_microdiff(sign_finish, sign_start); + time_ms = time_us / 1000; + fprintf(out, "Signing time in seconds: %7u.%03u\n", + (unsigned int)(time_ms / 1000), (unsigned int)(time_ms % 1000)); + if (time_us > 0) { + sig_ms = ((uint64_t)nsigned * 1000000000) / time_us; + fprintf(out, "Signatures per second: %7u.%03u\n", + (unsigned int)sig_ms / 1000, + (unsigned int)sig_ms % 1000); + } + + time_us = isc_time_microdiff(timer_finish, timer_start); + time_ms = time_us / 1000; + fprintf(out, "Runtime in seconds: %7u.%03u\n", + (unsigned int)(time_ms / 1000), (unsigned int)(time_ms % 1000)); +} + +int +main(int argc, char *argv[]) { + int i, ch; + char *startstr = NULL, *endstr = NULL, *classname = NULL; + char *dnskey_endstr = NULL; + char *origin = NULL, *file = NULL, *output = NULL; + char *inputformatstr = NULL, *outputformatstr = NULL; + char *serialformatstr = NULL; + char *dskeyfile[MAXDSKEYS]; + int ndskeys = 0; + char *endp; + isc_time_t timer_start, timer_finish; + isc_time_t sign_start, sign_finish; + dns_dnsseckey_t *key; + isc_result_t result, vresult; + isc_log_t *log = NULL; + const char *engine = NULL; + bool free_output = false; + int tempfilelen = 0; + dns_rdataclass_t rdclass; + isc_task_t **tasks = NULL; + hashlist_t hashlist; + bool make_keyset = false; + bool set_salt = false; + bool set_optout = false; + bool set_iter = false; + bool nonsecify = false; + + atomic_init(&shuttingdown, false); + atomic_init(&finished, false); + + /* Unused letters: Bb G J q Yy (and F is reserved). */ +#define CMDLINE_FLAGS \ + "3:AaCc:Dd:E:e:f:FghH:i:I:j:K:k:L:l:m:M:n:N:o:O:PpQqRr:s:ST:tuUv:VX:" \ + "xzZ:" + + /* + * Process memory debugging argument first. + */ + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'm': + if (strcasecmp(isc_commandline_argument, "record") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } + if (strcasecmp(isc_commandline_argument, "trace") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } + if (strcasecmp(isc_commandline_argument, "usage") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + default: + break; + } + } + isc_commandline_reset = true; + + masterstyle = &dns_master_style_explicitttl; + + check_result(isc_app_start(), "isc_app_start"); + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case '3': + set_salt = true; + nsec_datatype = dns_rdatatype_nsec3; + if (strcmp(isc_commandline_argument, "-") != 0) { + isc_buffer_t target; + char *sarg; + + sarg = isc_commandline_argument; + isc_buffer_init(&target, saltbuf, + sizeof(saltbuf)); + result = isc_hex_decodestring(sarg, &target); + check_result(result, "isc_hex_decodestring(" + "salt)"); + salt_length = isc_buffer_usedlength(&target); + } + break; + + case 'A': + set_optout = true; + if (OPTOUT(nsec3flags)) { + nsec3flags &= ~DNS_NSEC3FLAG_OPTOUT; + } else { + nsec3flags |= DNS_NSEC3FLAG_OPTOUT; + } + break; + + case 'a': + tryverify = true; + break; + + case 'C': + make_keyset = true; + break; + + case 'c': + classname = isc_commandline_argument; + break; + + case 'd': + dsdir = isc_commandline_argument; + if (strlen(dsdir) == 0U) { + fatal("DS directory must be non-empty string"); + } + result = try_dir(dsdir); + if (result != ISC_R_SUCCESS) { + fatal("cannot open directory %s: %s", dsdir, + isc_result_totext(result)); + } + break; + + case 'D': + output_dnssec_only = true; + break; + + case 'E': + engine = isc_commandline_argument; + break; + + case 'e': + endstr = isc_commandline_argument; + break; + + case 'f': + output = isc_commandline_argument; + if (strcmp(output, "-") == 0) { + output_stdout = true; + } + break; + + case 'g': + generateds = true; + break; + + case 'H': + set_iter = true; + /* too-many is NOT DOCUMENTED */ + if (strcmp(isc_commandline_argument, "too-many") == 0) { + nsec3iter = 151; + no_max_check = true; + break; + } + nsec3iter = strtoul(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("iterations must be numeric"); + } + if (nsec3iter > 0xffffU) { + fatal("iterations too big"); + } + break; + + case 'I': + inputformatstr = isc_commandline_argument; + break; + + case 'i': + endp = NULL; + cycle = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0' || cycle < 0) { + fatal("cycle period must be numeric and " + "positive"); + } + break; + + case 'j': + endp = NULL; + jitter = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0' || jitter < 0) { + fatal("jitter must be numeric and positive"); + } + break; + + case 'K': + directory = isc_commandline_argument; + break; + + case 'k': + if (ndskeys == MAXDSKEYS) { + fatal("too many key-signing keys specified"); + } + dskeyfile[ndskeys++] = isc_commandline_argument; + break; + + case 'L': + snset = true; + endp = NULL; + serialnum = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "source serial number " + "must be numeric"); + exit(1); + } + break; + + case 'l': + fatal("-l option (DLV lookaside) is obsolete"); + break; + + case 'M': + endp = NULL; + set_maxttl = true; + maxttl = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fprintf(stderr, "maximum TTL " + "must be numeric"); + exit(1); + } + break; + + case 'm': + break; + + case 'N': + serialformatstr = isc_commandline_argument; + break; + + case 'n': + endp = NULL; + ntasks = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0' || ntasks > INT32_MAX) { + fatal("number of cpus must be numeric"); + } + break; + + case 'O': + outputformatstr = isc_commandline_argument; + break; + + case 'o': + origin = isc_commandline_argument; + break; + + case 'P': + disable_zone_check = true; + break; + + case 'p': + fatal("The -p option has been deprecated.\n"); + break; + + case 'Q': + remove_inactkeysigs = true; + break; + + case 'R': + remove_orphansigs = true; + break; + + case 'r': + fatal("The -r options has been deprecated.\n"); + break; + + case 'S': + smartsign = true; + break; + + case 's': + startstr = isc_commandline_argument; + break; + + case 'T': + endp = NULL; + set_keyttl = true; + keyttl = strtottl(isc_commandline_argument); + break; + + case 't': + printstats = true; + break; + + case 'U': /* Undocumented for testing only. */ + unknownalg = true; + break; + + case 'u': + update_chain = true; + break; + + case 'v': + endp = NULL; + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("verbose level must be numeric"); + } + break; + + case 'q': + quiet = true; + break; + + case 'X': + dnskey_endstr = isc_commandline_argument; + break; + + case 'x': + keyset_kskonly = true; + break; + + case 'z': + ignore_kskflag = true; + break; + + case 'F': + /* Reserved for FIPS mode */ + FALLTHROUGH; + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + case 'Z': /* Undocumented test options */ + if (!strcmp(isc_commandline_argument, "nonsecify")) { + nonsecify = true; + } + break; + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + result = dst_lib_init(mctx, engine); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", + isc_result_totext(result)); + } + + isc_stdtime_get(&now); + + if (startstr != NULL) { + starttime = strtotime(startstr, now, now, NULL); + } else { + starttime = now - 3600; /* Allow for some clock skew. */ + } + + if (endstr != NULL) { + endtime = strtotime(endstr, now, starttime, NULL); + } else { + endtime = starttime + (30 * 24 * 60 * 60); + } + + if (dnskey_endstr != NULL) { + dnskey_endtime = strtotime(dnskey_endstr, now, starttime, NULL); + if (endstr != NULL && dnskey_endtime == endtime) { + fprintf(stderr, "WARNING: -e and -X were both set, " + "but have identical values.\n"); + } + } else { + dnskey_endtime = endtime; + } + + if (cycle == -1) { + cycle = (endtime - starttime) / 4; + } + + if (ntasks == 0) { + ntasks = isc_os_ncpus() * 2; + } + vbprintf(4, "using %d cpus\n", ntasks); + + rdclass = strtoclass(classname); + + if (directory == NULL) { + directory = "."; + } + + setup_logging(mctx, &log); + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argc < 1) { + usage(); + } + + file = argv[0]; + + argc -= 1; + argv += 1; + + if (origin == NULL) { + origin = file; + } + + if (output == NULL) { + size_t size; + free_output = true; + size = strlen(file) + strlen(".signed") + 1; + output = isc_mem_allocate(mctx, size); + snprintf(output, size, "%s.signed", file); + } + + if (inputformatstr != NULL) { + if (strcasecmp(inputformatstr, "text") == 0) { + inputformat = dns_masterformat_text; + } else if (strcasecmp(inputformatstr, "raw") == 0) { + inputformat = dns_masterformat_raw; + } else if (strncasecmp(inputformatstr, "raw=", 4) == 0) { + inputformat = dns_masterformat_raw; + fprintf(stderr, "WARNING: input format version " + "ignored\n"); + } else { + fatal("unknown file format: %s", inputformatstr); + } + } + + if (outputformatstr != NULL) { + if (strcasecmp(outputformatstr, "text") == 0) { + outputformat = dns_masterformat_text; + } else if (strcasecmp(outputformatstr, "full") == 0) { + outputformat = dns_masterformat_text; + masterstyle = &dns_master_style_full; + } else if (strcasecmp(outputformatstr, "raw") == 0) { + outputformat = dns_masterformat_raw; + } else if (strncasecmp(outputformatstr, "raw=", 4) == 0) { + char *end; + + outputformat = dns_masterformat_raw; + rawversion = strtol(outputformatstr + 4, &end, 10); + if (end == outputformatstr + 4 || *end != '\0' || + rawversion > 1U) + { + fprintf(stderr, "unknown raw format version\n"); + exit(1); + } + } else { + fatal("unknown file format: %s", outputformatstr); + } + } + + if (serialformatstr != NULL) { + if (strcasecmp(serialformatstr, "keep") == 0) { + serialformat = SOA_SERIAL_KEEP; + } else if (strcasecmp(serialformatstr, "increment") == 0 || + strcasecmp(serialformatstr, "incr") == 0) + { + serialformat = SOA_SERIAL_INCREMENT; + } else if (strcasecmp(serialformatstr, "unixtime") == 0) { + serialformat = SOA_SERIAL_UNIXTIME; + } else if (strcasecmp(serialformatstr, "date") == 0) { + serialformat = SOA_SERIAL_DATE; + } else { + fatal("unknown soa serial format: %s", serialformatstr); + } + } + + if (output_dnssec_only && outputformat != dns_masterformat_text) { + fatal("option -D can only be used with \"-O text\""); + } + + if (output_dnssec_only && serialformat != SOA_SERIAL_KEEP) { + fatal("option -D can only be used with \"-N keep\""); + } + + if (output_dnssec_only && set_maxttl) { + fatal("option -D cannot be used with -M"); + } + + result = dns_master_stylecreate(&dsstyle, DNS_STYLEFLAG_NO_TTL, 0, 24, + 0, 0, 0, 8, 0xffffffff, mctx); + check_result(result, "dns_master_stylecreate"); + + gdb = NULL; + TIME_NOW(&timer_start); + loadzone(file, origin, rdclass, &gdb); + gorigin = dns_db_origin(gdb); + gclass = dns_db_class(gdb); + get_soa_ttls(); + + if (set_maxttl && set_keyttl && keyttl > maxttl) { + fprintf(stderr, + "%s: warning: Specified key TTL %u " + "exceeds maximum zone TTL; reducing to %u\n", + program, keyttl, maxttl); + keyttl = maxttl; + } + + if (!set_keyttl) { + keyttl = soa_ttl; + } + + /* + * Check for any existing NSEC3 parameters in the zone, + * and use them as defaults if -u was not specified. + */ + if (update_chain && !set_optout && !set_iter && !set_salt) { + nsec_datatype = dns_rdatatype_nsec; + } else { + set_nsec3params(update_chain, set_salt, set_optout, set_iter); + } + + /* + * We need to do this early on, as we start messing with the list + * of keys rather early. + */ + ISC_LIST_INIT(keylist); + isc_rwlock_init(&keylist_lock, 0, 0); + + /* + * Fill keylist with: + * 1) Keys listed in the DNSKEY set that have + * private keys associated, *if* no keys were + * set on the command line. + * 2) ZSKs set on the command line + * 3) KSKs set on the command line + * 4) Any keys remaining in the DNSKEY set which + * do not have private keys associated and were + * not specified on the command line. + */ + if (argc == 0 || smartsign) { + loadzonekeys(!smartsign, false); + } + loadexplicitkeys(argv, argc, false); + loadexplicitkeys(dskeyfile, ndskeys, true); + loadzonekeys(!smartsign, true); + + /* + * If we're doing smart signing, look in the key repository for + * key files with metadata, and merge them with the keylist + * we have now. + */ + if (smartsign) { + build_final_keylist(); + } + + /* Now enumerate the key list */ + for (key = ISC_LIST_HEAD(keylist); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + key->index = keycount++; + } + + if (keycount == 0) { + if (disable_zone_check) { + fprintf(stderr, + "%s: warning: No keys specified " + "or found\n", + program); + } else { + fatal("No signing keys specified or found."); + } + nokeys = true; + } + + warnifallksk(gdb); + + if (IS_NSEC3) { + bool answer; + + hash_length = dns_nsec3_hashlength(dns_hash_sha1); + hashlist_init(&hashlist, + dns_db_nodecount(gdb, dns_dbtree_main) * 2, + hash_length); + result = dns_nsec_nseconly(gdb, gversion, NULL, &answer); + if (result == ISC_R_NOTFOUND) { + fprintf(stderr, + "%s: warning: NSEC3 generation " + "requested with no DNSKEY; ignoring\n", + program); + } else if (result != ISC_R_SUCCESS) { + check_result(result, "dns_nsec_nseconly"); + } else if (answer) { + fatal("NSEC3 generation requested with " + "NSEC-only DNSKEY"); + } + + if (nsec3iter > dns_nsec3_maxiterations()) { + if (no_max_check) { + fprintf(stderr, + "Ignoring max iterations check.\n"); + } else { + fatal("NSEC3 iterations too big. Maximum " + "iterations allowed %u.", + dns_nsec3_maxiterations()); + } + } + } else { + hashlist_init(&hashlist, 0, 0); /* silence clang */ + } + + gversion = NULL; + result = dns_db_newversion(gdb, &gversion); + check_result(result, "dns_db_newversion()"); + + switch (serialformat) { + case SOA_SERIAL_INCREMENT: + setsoaserial(0, dns_updatemethod_increment); + break; + case SOA_SERIAL_UNIXTIME: + setsoaserial(now, dns_updatemethod_unixtime); + break; + case SOA_SERIAL_DATE: + setsoaserial(now, dns_updatemethod_date); + break; + case SOA_SERIAL_KEEP: + default: + /* do nothing */ + break; + } + + /* Remove duplicates and cap TTLs at maxttl */ + cleanup_zone(); + + if (!nonsecify) { + if (IS_NSEC3) { + nsec3ify(dns_hash_sha1, nsec3iter, gsalt, salt_length, + &hashlist); + } else { + nsecify(); + } + } + + if (!nokeys) { + writeset("dsset-", dns_rdatatype_ds); + if (make_keyset) { + writeset("keyset-", dns_rdatatype_dnskey); + } + } + + if (output_stdout) { + outfp = stdout; + if (outputformatstr == NULL) { + masterstyle = &dns_master_style_full; + } + } else { + tempfilelen = strlen(output) + 20; + tempfile = isc_mem_get(mctx, tempfilelen); + + result = isc_file_mktemplate(output, tempfile, tempfilelen); + check_result(result, "isc_file_mktemplate"); + + if (outputformat == dns_masterformat_text) { + result = isc_file_openunique(tempfile, &outfp); + } else { + result = isc_file_bopenunique(tempfile, &outfp); + } + if (result != ISC_R_SUCCESS) { + fatal("failed to open temporary output file: %s", + isc_result_totext(result)); + } + removefile = true; + setfatalcallback(&removetempfile); + } + + print_time(outfp); + print_version(outfp); + + isc_managers_create(mctx, ntasks, 0, &netmgr, &taskmgr, NULL); + + main_task = NULL; + result = isc_task_create(taskmgr, 0, &main_task); + if (result != ISC_R_SUCCESS) { + fatal("failed to create task: %s", isc_result_totext(result)); + } + + tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *)); + for (i = 0; i < (int)ntasks; i++) { + tasks[i] = NULL; + result = isc_task_create(taskmgr, 0, &tasks[i]); + if (result != ISC_R_SUCCESS) { + fatal("failed to create task: %s", + isc_result_totext(result)); + } + } + + isc_mutex_init(&namelock); + + if (printstats) { + isc_mutex_init(&statslock); + } + + presign(); + TIME_NOW(&sign_start); + signapex(); + if (!atomic_load(&finished)) { + /* + * There is more work to do. Spread it out over multiple + * processors if possible. + */ + for (i = 0; i < (int)ntasks; i++) { + result = isc_app_onrun(mctx, main_task, startworker, + tasks[i]); + if (result != ISC_R_SUCCESS) { + fatal("failed to start task: %s", + isc_result_totext(result)); + } + } + (void)isc_app_run(); + if (!atomic_load(&finished)) { + fatal("process aborted by user"); + } + } else { + isc_task_detach(&main_task); + } + atomic_store(&shuttingdown, true); + for (i = 0; i < (int)ntasks; i++) { + isc_task_detach(&tasks[i]); + } + isc_managers_destroy(&netmgr, &taskmgr, NULL); + isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *)); + postsign(); + TIME_NOW(&sign_finish); + + if (disable_zone_check) { + vresult = ISC_R_SUCCESS; + } else { + vresult = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, + NULL, mctx, ignore_kskflag, + keyset_kskonly, report); + if (vresult != ISC_R_SUCCESS) { + fprintf(output_stdout ? stderr : stdout, + "Zone verification failed (%s)\n", + isc_result_totext(vresult)); + } + } + + if (outputformat != dns_masterformat_text) { + dns_masterrawheader_t header; + dns_master_initrawheader(&header); + if (rawversion == 0U) { + header.flags = DNS_MASTERRAW_COMPAT; + } else if (snset) { + header.flags = DNS_MASTERRAW_SOURCESERIALSET; + header.sourceserial = serialnum; + } + result = dns_master_dumptostream(mctx, gdb, gversion, + masterstyle, outputformat, + &header, outfp); + check_result(result, "dns_master_dumptostream3"); + } + + isc_mutex_destroy(&namelock); + if (printstats) { + isc_mutex_destroy(&statslock); + } + + if (!output_stdout) { + result = isc_stdio_close(outfp); + check_result(result, "isc_stdio_close"); + removefile = false; + + if (vresult == ISC_R_SUCCESS) { + result = isc_file_rename(tempfile, output); + if (result != ISC_R_SUCCESS) { + fatal("failed to rename temp file to %s: %s", + output, isc_result_totext(result)); + } + printf("%s\n", output); + } else { + isc_file_remove(tempfile); + } + } + + dns_db_closeversion(gdb, &gversion, false); + dns_db_detach(&gdb); + + hashlist_free(&hashlist); + + while (!ISC_LIST_EMPTY(keylist)) { + key = ISC_LIST_HEAD(keylist); + ISC_LIST_UNLINK(keylist, key, link); + dns_dnsseckey_destroy(mctx, &key); + } + + if (tempfilelen != 0) { + isc_mem_put(mctx, tempfile, tempfilelen); + } + + if (free_output) { + isc_mem_free(mctx, output); + } + + dns_master_styledestroy(&dsstyle, mctx); + + cleanup_logging(&log); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + + (void)isc_app_finish(); + + if (printstats) { + TIME_NOW(&timer_finish); + print_stats(&timer_start, &timer_finish, &sign_start, + &sign_finish); + } + + return (vresult == ISC_R_SUCCESS ? 0 : 1); +} diff --git a/bin/dnssec/dnssec-signzone.rst b/bin/dnssec/dnssec-signzone.rst new file mode 100644 index 0000000..668d7f3 --- /dev/null +++ b/bin/dnssec/dnssec-signzone.rst @@ -0,0 +1,436 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-signzone +.. program:: dnssec-signzone +.. _man_dnssec-signzone: + +dnssec-signzone - DNSSEC zone signing tool +------------------------------------------ + +Synopsis +~~~~~~~~ + +:program:`dnssec-signzone` [**-a**] [**-c** class] [**-d** directory] [**-D**] [**-E** engine] [**-e** end-time] [**-f** output-file] [**-g**] [**-h**] [**-i** interval] [**-I** input-format] [**-j** jitter] [**-K** directory] [**-k** key] [**-L** serial] [**-M** maxttl] [**-N** soa-serial-format] [**-o** origin] [**-O** output-format] [**-P**] [**-Q**] [**-q**] [**-R**] [**-S**] [**-s** start-time] [**-T** ttl] [**-t**] [**-u**] [**-v** level] [**-V**] [**-X** extended end-time] [**-x**] [**-z**] [**-3** salt] [**-H** iterations] [**-A**] {zonefile} [key...] + +Description +~~~~~~~~~~~ + +:program:`dnssec-signzone` signs a zone; it generates NSEC and RRSIG records +and produces a signed version of the zone. The security status of +delegations from the signed zone (that is, whether the child zones are +secure) is determined by the presence or absence of a ``keyset`` +file for each child zone. + +Options +~~~~~~~ + +.. option:: -a + + This option verifies all generated signatures. + +.. option:: -c class + + This option specifies the DNS class of the zone. + +.. option:: -C + + This option sets compatibility mode, in which a ``keyset-zonename`` file is generated in addition + to ``dsset-zonename`` when signing a zone, for use by older versions + of :program:`dnssec-signzone`. + +.. option:: -d directory + + This option indicates the directory where BIND 9 should look for ``dsset-`` or ``keyset-`` files. + +.. option:: -D + + This option indicates that only those record types automatically managed by + :program:`dnssec-signzone`, i.e., RRSIG, NSEC, NSEC3 and NSEC3PARAM records, should be included in the output. + If smart signing (:option:`-S`) is used, DNSKEY records are also included. + The resulting file can be included in the original zone file with + ``$INCLUDE``. This option cannot be combined with :option:`-O raw <-O>` + or serial-number updating. + +.. option:: -E engine + + This option specifies the hardware to use for cryptographic + operations, such as a secure key store used for signing, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -g + + This option indicates that DS records for child zones should be generated from a ``dsset-`` or ``keyset-`` + file. Existing DS records are removed. + +.. option:: -K directory + + This option specifies the directory to search for DNSSEC keys. If not + specified, it defaults to the current directory. + +.. option:: -k key + + This option tells BIND 9 to treat the specified key as a key-signing key, ignoring any key flags. This + option may be specified multiple times. + +.. option:: -M maxttl + + This option sets the maximum TTL for the signed zone. Any TTL higher than ``maxttl`` + in the input zone is reduced to ``maxttl`` in the output. This + provides certainty as to the largest possible TTL in the signed zone, + which is useful to know when rolling keys. The maxttl is the longest + possible time before signatures that have been retrieved by resolvers + expire from resolver caches. Zones that are signed with this + option should be configured to use a matching ``max-zone-ttl`` in + :iscman:`named.conf`. (Note: This option is incompatible with :option:`-D`, + because it modifies non-DNSSEC data in the output zone.) + +.. option:: -s start-time + + This option specifies the date and time when the generated RRSIG records become + valid. This can be either an absolute or relative time. An absolute + start time is indicated by a number in YYYYMMDDHHMMSS notation; + 20000530144500 denotes 14:45:00 UTC on May 30th, 2000. A relative + start time is indicated by ``+N``, which is N seconds from the current + time. If no ``start-time`` is specified, the current time minus 1 + hour (to allow for clock skew) is used. + +.. option:: -e end-time + + This option specifies the date and time when the generated RRSIG records expire. As + with ``start-time``, an absolute time is indicated in YYYYMMDDHHMMSS + notation. A time relative to the start time is indicated with ``+N``, + which is N seconds from the start time. A time relative to the + current time is indicated with ``now+N``. If no ``end-time`` is + specified, 30 days from the start time is the default. + ``end-time`` must be later than ``start-time``. + +.. option:: -X extended end-time + + This option specifies the date and time when the generated RRSIG records for the + DNSKEY RRset expire. This is to be used in cases when the DNSKEY + signatures need to persist longer than signatures on other records; + e.g., when the private component of the KSK is kept offline and the + KSK signature is to be refreshed manually. + + As with ``end-time``, an absolute time is indicated in + YYYYMMDDHHMMSS notation. A time relative to the start time is + indicated with ``+N``, which is N seconds from the start time. A time + relative to the current time is indicated with ``now+N``. If no + ``extended end-time`` is specified, the value of ``end-time`` is used + as the default. (``end-time``, in turn, defaults to 30 days from the + start time.) ``extended end-time`` must be later than ``start-time``. + +.. option:: -f output-file + + This option indicates the name of the output file containing the signed zone. The default + is to append ``.signed`` to the input filename. If ``output-file`` is + set to ``-``, then the signed zone is written to the standard + output, with a default output format of ``full``. + +.. option:: -h + + This option prints a short summary of the options and arguments to + :program:`dnssec-signzone`. + +.. option:: -V + + This option prints version information. + +.. option:: -i interval + + This option indicates that, when a previously signed zone is passed as input, records may be + re-signed. The ``interval`` option specifies the cycle interval as an + offset from the current time, in seconds. If a RRSIG record expires + after the cycle interval, it is retained; otherwise, it is considered + to be expiring soon and it is replaced. + + The default cycle interval is one quarter of the difference between + the signature end and start times. So if neither ``end-time`` nor + ``start-time`` is specified, :program:`dnssec-signzone` generates + signatures that are valid for 30 days, with a cycle interval of 7.5 + days. Therefore, if any existing RRSIG records are due to expire in + less than 7.5 days, they are replaced. + +.. option:: -I input-format + + This option sets the format of the input zone file. Possible formats are + ``text`` (the default), and ``raw``. This option is primarily + intended to be used for dynamic signed zones, so that the dumped zone + file in a non-text format containing updates can be signed directly. + This option is not useful for non-dynamic zones. + +.. option:: -j jitter + + When signing a zone with a fixed signature lifetime, all RRSIG + records issued at the time of signing expire simultaneously. If the + zone is incrementally signed, i.e., a previously signed zone is passed + as input to the signer, all expired signatures must be regenerated + at approximately the same time. The ``jitter`` option specifies a jitter + window that is used to randomize the signature expire time, thus + spreading incremental signature regeneration over time. + + Signature lifetime jitter also, to some extent, benefits validators and + servers by spreading out cache expiration, i.e., if large numbers of + RRSIGs do not expire at the same time from all caches, there is + less congestion than if all validators need to refetch at around the + same time. + +.. option:: -L serial + + When writing a signed zone to "raw" format, this option sets the "source + serial" value in the header to the specified ``serial`` number. (This is + expected to be used primarily for testing purposes.) + +.. option:: -n ncpus + + This option specifies the number of threads to use. By default, one thread is + started for each detected CPU. + +.. option:: -N soa-serial-format + + This option sets the SOA serial number format of the signed zone. Possible formats are + ``keep`` (the default), ``increment``, ``unixtime``, and + ``date``. + + **keep** + This format indicates that the SOA serial number should not be modified. + + **increment** + This format increments the SOA serial number using :rfc:`1982` arithmetic. + + **unixtime** + This format sets the SOA serial number to the number of seconds + since the beginning of the Unix epoch, unless the serial + number is already greater than or equal to that value, in + which case it is simply incremented by one. + + **date** + This format sets the SOA serial number to today's date, in + YYYYMMDDNN format, unless the serial number is already greater + than or equal to that value, in which case it is simply + incremented by one. + +.. option:: -o origin + + This option sets the zone origin. If not specified, the name of the zone file is + assumed to be the origin. + +.. option:: -O output-format + + This option sets the format of the output file containing the signed + zone. Possible formats are ``text`` (the default), which is the standard + textual representation of the zone; ``full``, which is text output in a + format suitable for processing by external scripts; and ``raw`` and + ``raw=N``, which store the zone in binary formats for rapid loading by + :iscman:`named`. ``raw=N`` specifies the format version of the raw zone file: + if N is 0, the raw file can be read by any version of :iscman:`named`; if N is + 1, the file can be read by release 9.9.0 or higher. The default is 1. + +.. option:: -P + + This option disables post-sign verification tests. + + The post-sign verification tests ensure that for each algorithm in + use there is at least one non-revoked self-signed KSK key, that all + revoked KSK keys are self-signed, and that all records in the zone + are signed by the algorithm. This option skips these tests. + +.. option:: -Q + + This option removes signatures from keys that are no longer active. + + Normally, when a previously signed zone is passed as input to the + signer, and a DNSKEY record has been removed and replaced with a new + one, signatures from the old key that are still within their validity + period are retained. This allows the zone to continue to validate + with cached copies of the old DNSKEY RRset. The :option:`-Q` option forces + :program:`dnssec-signzone` to remove signatures from keys that are no longer + active. This enables ZSK rollover using the procedure described in + :rfc:`4641#4.2.1.1` ("Pre-Publish Key Rollover"). + +.. option:: -q + + This option enables quiet mode, which suppresses unnecessary output. Without this option, when + :program:`dnssec-signzone` is run it prints three pieces of information to standard output: the number of + keys in use; the algorithms used to verify the zone was signed correctly and + other status information; and the filename containing the signed + zone. With the option that output is suppressed, leaving only the filename. + +.. option:: -R + + This option removes signatures from keys that are no longer published. + + This option is similar to :option:`-Q`, except it forces + :program:`dnssec-signzone` to remove signatures from keys that are no longer + published. This enables ZSK rollover using the procedure described in + :rfc:`4641#4.2.1.2` ("Double Signature Zone Signing Key + Rollover"). + +.. option:: -S + + This option enables smart signing, which instructs :program:`dnssec-signzone` to search the key + repository for keys that match the zone being signed, and to include + them in the zone if appropriate. + + When a key is found, its timing metadata is examined to determine how + it should be used, according to the following rules. Each successive + rule takes priority over the prior ones: + + If no timing metadata has been set for the key, the key is + published in the zone and used to sign the zone. + + If the key's publication date is set and is in the past, the key + is published in the zone. + + If the key's activation date is set and is in the past, the key is + published (regardless of publication date) and used to sign the + zone. + + If the key's revocation date is set and is in the past, and the key + is published, then the key is revoked, and the revoked key is used + to sign the zone. + + If either the key's unpublication or deletion date is set and + in the past, the key is NOT published or used to sign the zone, + regardless of any other metadata. + + If the key's sync publication date is set and is in the past, + synchronization records (type CDS and/or CDNSKEY) are created. + + If the key's sync deletion date is set and is in the past, + synchronization records (type CDS and/or CDNSKEY) are removed. + +.. option:: -T ttl + + This option specifies a TTL to be used for new DNSKEY records imported into the + zone from the key repository. If not specified, the default is the + TTL value from the zone's SOA record. This option is ignored when + signing without :option:`-S`, since DNSKEY records are not imported from + the key repository in that case. It is also ignored if there are any + pre-existing DNSKEY records at the zone apex, in which case new + records' TTL values are set to match them, or if any of the + imported DNSKEY records had a default TTL value. In the event of a + conflict between TTL values in imported keys, the shortest one is + used. + +.. option:: -t + + This option prints statistics at completion. + +.. option:: -u + + This option updates the NSEC/NSEC3 chain when re-signing a previously signed zone. + With this option, a zone signed with NSEC can be switched to NSEC3, + or a zone signed with NSEC3 can be switched to NSEC or to NSEC3 with + different parameters. Without this option, :program:`dnssec-signzone` + retains the existing chain when re-signing. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -x + + This option indicates that BIND 9 should only sign the DNSKEY, CDNSKEY, and CDS RRsets with key-signing keys, + and should omit signatures from zone-signing keys. (This is similar to the + ``dnssec-dnskey-kskonly yes;`` zone option in :iscman:`named`.) + +.. option:: -z + + This option indicates that BIND 9 should ignore the KSK flag on keys when determining what to sign. This causes + KSK-flagged keys to sign all records, not just the DNSKEY RRset. + (This is similar to the ``update-check-ksk no;`` zone option in + :iscman:`named`.) + +.. option:: -3 salt + + This option generates an NSEC3 chain with the given hex-encoded salt. A dash + (-) can be used to indicate that no salt is to be used when + generating the NSEC3 chain. + + .. note:: + ``-3 -`` is the recommended configuration. Adding salt provides no practical benefits. + +.. option:: -H iterations + + This option indicates that, when generating an NSEC3 chain, BIND 9 should use this many iterations. The default + is 0. + + .. warning:: + Values greater than 0 cause interoperability issues and also increase the risk of CPU-exhausting DoS attacks. + +.. option:: -A + + This option indicates that, when generating an NSEC3 chain, BIND 9 should set the OPTOUT flag on all NSEC3 + records and should not generate NSEC3 records for insecure delegations. + + .. warning:: + Do not use this option unless all its implications are fully understood. This option is intended only for extremely large zones (comparable to ``com.``) with sparse secure delegations. + +.. option:: -AA + + This option turns the OPTOUT flag off for + all records. This is useful when using the :option:`-u` option to modify an + NSEC3 chain which previously had OPTOUT set. + +.. option:: zonefile + + This option sets the file containing the zone to be signed. + +.. option:: key + + This option specifies which keys should be used to sign the zone. If no keys are + specified, the zone is examined for DNSKEY records at the + zone apex. If these records are found and there are matching private keys in + the current directory, they are used for signing. + +Example +~~~~~~~ + +The following command signs the ``example.com`` zone with the +ECDSAP256SHA256 key generated by :iscman:`dnssec-keygen` +(Kexample.com.+013+17247). Because the :option:`-S` option is not being used, +the zone's keys must be in the master file (``db.example.com``). This +invocation looks for ``dsset`` files in the current directory, so that +DS records can be imported from them (:option:`-g`). + +:: + + % dnssec-signzone -g -o example.com db.example.com \ + Kexample.com.+013+17247 + db.example.com.signed + % + +In the above example, :program:`dnssec-signzone` creates the file +``db.example.com.signed``. This file should be referenced in a zone +statement in the :iscman:`named.conf` file. + +This example re-signs a previously signed zone with default parameters. +The private keys are assumed to be in the current directory. + +:: + + % cp db.example.com.signed db.example.com + % dnssec-signzone -o example.com db.example.com + db.example.com.signed + % + +See Also +~~~~~~~~ + +:iscman:`dnssec-keygen(8) `, BIND 9 Administrator Reference Manual, :rfc:`4033`, +:rfc:`4641`. diff --git a/bin/dnssec/dnssec-verify.c b/bin/dnssec/dnssec-verify.c new file mode 100644 index 0000000..5f2f2d1 --- /dev/null +++ b/bin/dnssec/dnssec-verify.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnssectool.h" + +const char *program = "dnssec-verify"; + +static isc_stdtime_t now; +static isc_mem_t *mctx = NULL; +static dns_masterformat_t inputformat = dns_masterformat_text; +static dns_db_t *gdb; /* The database */ +static dns_dbversion_t *gversion; /* The database version */ +static dns_rdataclass_t gclass; /* The class */ +static dns_name_t *gorigin; /* The database origin */ +static bool ignore_kskflag = false; +static bool keyset_kskonly = false; + +static void +report(const char *format, ...) { + if (!quiet) { + char buf[4096]; + va_list args; + + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + fprintf(stdout, "%s\n", buf); + } +} + +/*% + * Load the zone file from disk + */ +static void +loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) { + isc_buffer_t b; + int len; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result; + + len = strlen(origin); + isc_buffer_init(&b, origin, len); + isc_buffer_add(&b, len); + + name = dns_fixedname_initname(&fname); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fatal("failed converting name '%s' to dns format: %s", origin, + isc_result_totext(result)); + } + + result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone, rdclass, 0, + NULL, db); + check_result(result, "dns_db_create()"); + + result = dns_db_load(*db, file, inputformat, 0); + switch (result) { + case DNS_R_SEENINCLUDE: + case ISC_R_SUCCESS: + break; + case DNS_R_NOTZONETOP: + /* + * Comparing pointers (vs. using strcmp()) is intentional: we + * want to check whether -o was supplied on the command line, + * not whether origin and file contain the same string. + */ + if (origin == file) { + fatal("failed loading zone '%s' from file '%s': " + "use -o to specify a different zone origin", + origin, file); + } + FALLTHROUGH; + default: + fatal("failed loading zone from '%s': %s", file, + isc_result_totext(result)); + } +} + +noreturn static void +usage(void); + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\t%s [options] zonefile [keys]\n", program); + + fprintf(stderr, "\n"); + + fprintf(stderr, "Version: %s\n", PACKAGE_VERSION); + + fprintf(stderr, "Options: (default value in parenthesis) \n"); + fprintf(stderr, "\t-v debuglevel (0)\n"); + fprintf(stderr, "\t-q quiet\n"); + fprintf(stderr, "\t-V:\tprint version information\n"); + fprintf(stderr, "\t-o origin:\n"); + fprintf(stderr, "\t\tzone origin (name of zonefile)\n"); + fprintf(stderr, "\t-I format:\n"); + fprintf(stderr, "\t\tfile format of input zonefile (text)\n"); + fprintf(stderr, "\t-c class (IN)\n"); + fprintf(stderr, "\t-E engine:\n"); + fprintf(stderr, "\t\tname of an OpenSSL engine to use\n"); + fprintf(stderr, "\t-x:\tDNSKEY record signed with KSKs only, " + "not ZSKs\n"); + fprintf(stderr, "\t-z:\tAll records signed with KSKs\n"); + exit(0); +} + +int +main(int argc, char *argv[]) { + char *origin = NULL, *file = NULL; + char *inputformatstr = NULL; + isc_result_t result; + isc_log_t *log = NULL; + const char *engine = NULL; + char *classname = NULL; + dns_rdataclass_t rdclass; + char *endp; + int ch; + +#define CMDLINE_FLAGS "c:E:hm:o:I:qv:Vxz" + + /* + * Process memory debugging argument first. + */ + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'm': + if (strcasecmp(isc_commandline_argument, "record") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } + if (strcasecmp(isc_commandline_argument, "trace") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } + if (strcasecmp(isc_commandline_argument, "usage") == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + default: + break; + } + } + isc_commandline_reset = true; + check_result(isc_app_start(), "isc_app_start"); + + isc_mem_create(&mctx); + + isc_commandline_errprint = false; + + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case 'c': + classname = isc_commandline_argument; + break; + + case 'E': + engine = isc_commandline_argument; + break; + + case 'I': + inputformatstr = isc_commandline_argument; + break; + + case 'm': + break; + + case 'o': + origin = isc_commandline_argument; + break; + + case 'v': + endp = NULL; + verbose = strtol(isc_commandline_argument, &endp, 0); + if (*endp != '\0') { + fatal("verbose level must be numeric"); + } + break; + + case 'q': + quiet = true; + break; + + case 'x': + keyset_kskonly = true; + break; + + case 'z': + ignore_kskflag = true; + break; + + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + } + FALLTHROUGH; + + case 'h': + /* Does not return. */ + usage(); + + case 'V': + /* Does not return. */ + version(program); + + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + result = dst_lib_init(mctx, engine); + if (result != ISC_R_SUCCESS) { + fatal("could not initialize dst: %s", + isc_result_totext(result)); + } + + isc_stdtime_get(&now); + + rdclass = strtoclass(classname); + + setup_logging(mctx, &log); + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argc < 1) { + usage(); + } + + file = argv[0]; + + argc -= 1; + argv += 1; + + POST(argc); + POST(argv); + + if (origin == NULL) { + origin = file; + } + + if (inputformatstr != NULL) { + if (strcasecmp(inputformatstr, "text") == 0) { + inputformat = dns_masterformat_text; + } else if (strcasecmp(inputformatstr, "raw") == 0) { + inputformat = dns_masterformat_raw; + } else { + fatal("unknown file format: %s\n", inputformatstr); + } + } + + gdb = NULL; + report("Loading zone '%s' from file '%s'\n", origin, file); + loadzone(file, origin, rdclass, &gdb); + gorigin = dns_db_origin(gdb); + gclass = dns_db_class(gdb); + + gversion = NULL; + result = dns_db_newversion(gdb, &gversion); + check_result(result, "dns_db_newversion()"); + + result = dns_zoneverify_dnssec(NULL, gdb, gversion, gorigin, NULL, mctx, + ignore_kskflag, keyset_kskonly, report); + + dns_db_closeversion(gdb, &gversion, false); + dns_db_detach(&gdb); + + cleanup_logging(&log); + dst_lib_destroy(); + if (verbose > 10) { + isc_mem_stats(mctx, stdout); + } + isc_mem_destroy(&mctx); + + (void)isc_app_finish(); + + return (result == ISC_R_SUCCESS ? 0 : 1); +} diff --git a/bin/dnssec/dnssec-verify.rst b/bin/dnssec/dnssec-verify.rst new file mode 100644 index 0000000..8700894 --- /dev/null +++ b/bin/dnssec/dnssec-verify.rst @@ -0,0 +1,107 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: dnssec-verify +.. program:: dnssec-verify +.. _man_dnssec-verify: + +dnssec-verify - DNSSEC zone verification tool +--------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`dnssec-verify` [**-c** class] [**-E** engine] [**-I** input-format] [**-o** origin] [**-q**] [**-v** level] [**-V**] [**-x**] [**-z**] {zonefile} + +Description +~~~~~~~~~~~ + +:program:`dnssec-verify` verifies that a zone is fully signed for each +algorithm found in the DNSKEY RRset for the zone, and that the +NSEC/NSEC3 chains are complete. + +Options +~~~~~~~ + +.. option:: -c class + + This option specifies the DNS class of the zone. + +.. option:: -E engine + + This option specifies the cryptographic hardware to use, when applicable. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -I input-format + + This option sets the format of the input zone file. Possible formats are ``text`` + (the default) and ``raw``. This option is primarily intended to be used + for dynamic signed zones, so that the dumped zone file in a non-text + format containing updates can be verified independently. + This option is not useful for non-dynamic zones. + +.. option:: -o origin + + This option indicates the zone origin. If not specified, the name of the zone file is + assumed to be the origin. + +.. option:: -v level + + This option sets the debugging level. + +.. option:: -V + + This option prints version information. + +.. option:: -q + + This option sets quiet mode, which suppresses output. Without this option, when :program:`dnssec-verify` + is run it prints to standard output the number of keys in use, the + algorithms used to verify the zone was signed correctly, and other status + information. With this option, all non-error output is suppressed, and only the exit + code indicates success. + +.. option:: -x + + This option verifies only that the DNSKEY RRset is signed with key-signing keys. + Without this flag, it is assumed that the DNSKEY RRset is signed + by all active keys. When this flag is set, it is not an error if + the DNSKEY RRset is not signed by zone-signing keys. This corresponds + to the :option:`-x option in dnssec-signzone `. + +.. option:: -z + + This option indicates that the KSK flag on the keys should be ignored when determining whether the zone is + correctly signed. Without this flag, it is assumed that there is + a non-revoked, self-signed DNSKEY with the KSK flag set for each + algorithm, and that RRsets other than DNSKEY RRset are signed with + a different DNSKEY without the KSK flag set. + + With this flag set, BIND 9 only requires that for each algorithm, there + be at least one non-revoked, self-signed DNSKEY, regardless of + the KSK flag state, and that other RRsets be signed by a + non-revoked key for the same algorithm that includes the self-signed + key; the same key may be used for both purposes. This corresponds to + the :option:`-z option in dnssec-signzone `. + +.. option:: zonefile + + This option indicates the file containing the zone to be signed. + +See Also +~~~~~~~~ + +:iscman:`dnssec-signzone(8) `, BIND 9 Administrator Reference Manual, :rfc:`4033`. diff --git a/bin/dnssec/dnssectool.c b/bin/dnssec/dnssectool.c new file mode 100644 index 0000000..c07e091 --- /dev/null +++ b/bin/dnssec/dnssectool.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/*% + * DNSSEC Support Routines. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dnssectool.h" + +#define KEYSTATES_NVALUES 4 +static const char *keystates[KEYSTATES_NVALUES] = { + "hidden", + "rumoured", + "omnipresent", + "unretentive", +}; + +int verbose = 0; +bool quiet = false; +dns_dsdigest_t dtype[8]; + +static fatalcallback_t *fatalcallback = NULL; + +void +fatal(const char *format, ...) { + va_list args; + + fprintf(stderr, "%s: fatal: ", program); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + if (fatalcallback != NULL) { + (*fatalcallback)(); + } + exit(1); +} + +void +setfatalcallback(fatalcallback_t *callback) { + fatalcallback = callback; +} + +void +check_result(isc_result_t result, const char *message) { + if (result != ISC_R_SUCCESS) { + fatal("%s: %s", message, isc_result_totext(result)); + } +} + +void +vbprintf(int level, const char *fmt, ...) { + va_list ap; + if (level > verbose) { + return; + } + va_start(ap, fmt); + fprintf(stderr, "%s: ", program); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void +version(const char *name) { + fprintf(stderr, "%s %s\n", name, PACKAGE_VERSION); + exit(0); +} + +void +sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size) { + char namestr[DNS_NAME_FORMATSIZE]; + char algstr[DNS_NAME_FORMATSIZE]; + + dns_name_format(&sig->signer, namestr, sizeof(namestr)); + dns_secalg_format(sig->algorithm, algstr, sizeof(algstr)); + snprintf(cp, size, "%s/%s/%d", namestr, algstr, sig->keyid); +} + +void +setup_logging(isc_mem_t *mctx, isc_log_t **logp) { + isc_logdestination_t destination; + isc_logconfig_t *logconfig = NULL; + isc_log_t *log = NULL; + int level; + + if (verbose < 0) { + verbose = 0; + } + switch (verbose) { + case 0: + /* + * We want to see warnings about things like out-of-zone + * data in the master file even when not verbose. + */ + level = ISC_LOG_WARNING; + break; + case 1: + level = ISC_LOG_INFO; + break; + default: + level = ISC_LOG_DEBUG(verbose - 2 + 1); + break; + } + + isc_log_create(mctx, &log, &logconfig); + isc_log_setcontext(log); + dns_log_init(log); + dns_log_setcontext(log); + isc_log_settag(logconfig, program); + + /* + * Set up a channel similar to default_stderr except: + * - the logging level is passed in + * - the program name and logging level are printed + * - no time stamp is printed + */ + destination.file.stream = stderr; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, level, + &destination, + ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL); + + RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL) == + ISC_R_SUCCESS); + + *logp = log; +} + +void +cleanup_logging(isc_log_t **logp) { + isc_log_t *log; + + REQUIRE(logp != NULL); + + log = *logp; + *logp = NULL; + + if (log == NULL) { + return; + } + + isc_log_destroy(&log); + isc_log_setcontext(NULL); + dns_log_setcontext(NULL); +} + +static isc_stdtime_t +time_units(isc_stdtime_t offset, char *suffix, const char *str) { + switch (suffix[0]) { + case 'Y': + case 'y': + return (offset * (365 * 24 * 3600)); + case 'M': + case 'm': + switch (suffix[1]) { + case 'O': + case 'o': + return (offset * (30 * 24 * 3600)); + case 'I': + case 'i': + return (offset * 60); + case '\0': + fatal("'%s' ambiguous: use 'mi' for minutes " + "or 'mo' for months", + str); + default: + fatal("time value %s is invalid", str); + } + UNREACHABLE(); + break; + case 'W': + case 'w': + return (offset * (7 * 24 * 3600)); + case 'D': + case 'd': + return (offset * (24 * 3600)); + case 'H': + case 'h': + return (offset * 3600); + case 'S': + case 's': + case '\0': + return (offset); + default: + fatal("time value %s is invalid", str); + } + UNREACHABLE(); + return (0); /* silence compiler warning */ +} + +static bool +isnone(const char *str) { + return ((strcasecmp(str, "none") == 0) || + (strcasecmp(str, "never") == 0) || + (strcasecmp(str, "unset") == 0)); +} + +dns_ttl_t +strtottl(const char *str) { + const char *orig = str; + dns_ttl_t ttl; + char *endp; + + if (isnone(str)) { + return ((dns_ttl_t)0); + } + + ttl = strtol(str, &endp, 0); + if (ttl == 0 && endp == str) { + fatal("TTL must be numeric"); + } + ttl = time_units(ttl, endp, orig); + return (ttl); +} + +dst_key_state_t +strtokeystate(const char *str) { + if (isnone(str)) { + return (DST_KEY_STATE_NA); + } + + for (int i = 0; i < KEYSTATES_NVALUES; i++) { + if (keystates[i] != NULL && strcasecmp(str, keystates[i]) == 0) + { + return ((dst_key_state_t)i); + } + } + fatal("unknown key state %s", str); +} + +isc_stdtime_t +strtotime(const char *str, int64_t now, int64_t base, bool *setp) { + int64_t val, offset; + isc_result_t result; + const char *orig = str; + char *endp; + size_t n; + struct tm tm; + + if (isnone(str)) { + if (setp != NULL) { + *setp = false; + } + return ((isc_stdtime_t)0); + } + + if (setp != NULL) { + *setp = true; + } + + if ((str[0] == '0' || str[0] == '-') && str[1] == '\0') { + return ((isc_stdtime_t)0); + } + + /* + * We accept times in the following formats: + * now([+-]offset) + * YYYYMMDD([+-]offset) + * YYYYMMDDhhmmss([+-]offset) + * Day Mon DD HH:MM:SS YYYY([+-]offset) + * 1234567890([+-]offset) + * [+-]offset + */ + n = strspn(str, "0123456789"); + if ((n == 8u || n == 14u) && + (str[n] == '\0' || str[n] == '-' || str[n] == '+')) + { + char timestr[15]; + + strlcpy(timestr, str, sizeof(timestr)); + timestr[n] = 0; + if (n == 8u) { + strlcat(timestr, "000000", sizeof(timestr)); + } + result = dns_time64_fromtext(timestr, &val); + if (result != ISC_R_SUCCESS) { + fatal("time value %s is invalid: %s", orig, + isc_result_totext(result)); + } + base = val; + str += n; + } else if (n == 10u && + (str[n] == '\0' || str[n] == '-' || str[n] == '+')) + { + base = strtoll(str, &endp, 0); + str += 10; + } else if (strncmp(str, "now", 3) == 0) { + base = now; + str += 3; + } else if (str[0] >= 'A' && str[0] <= 'Z') { + /* parse ctime() format as written by `dnssec-settime -p` */ + endp = isc_tm_strptime(str, "%a %b %d %H:%M:%S %Y", &tm); + if (endp != str + 24) { + fatal("time value %s is invalid", orig); + } + base = mktime(&tm); + str += 24; + } + + if (str[0] == '\0') { + return ((isc_stdtime_t)base); + } else if (str[0] == '+') { + offset = strtol(str + 1, &endp, 0); + offset = time_units((isc_stdtime_t)offset, endp, orig); + val = base + offset; + } else if (str[0] == '-') { + offset = strtol(str + 1, &endp, 0); + offset = time_units((isc_stdtime_t)offset, endp, orig); + val = base - offset; + } else { + fatal("time value %s is invalid", orig); + } + + return ((isc_stdtime_t)val); +} + +dns_rdataclass_t +strtoclass(const char *str) { + isc_textregion_t r; + dns_rdataclass_t rdclass; + isc_result_t result; + + if (str == NULL) { + return (dns_rdataclass_in); + } + DE_CONST(str, r.base); + r.length = strlen(str); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + fatal("unknown class %s", str); + } + return (rdclass); +} + +unsigned int +strtodsdigest(const char *str) { + isc_textregion_t r; + dns_dsdigest_t alg; + isc_result_t result; + + DE_CONST(str, r.base); + r.length = strlen(str); + result = dns_dsdigest_fromtext(&alg, &r); + if (result != ISC_R_SUCCESS) { + fatal("unknown DS algorithm %s", str); + } + return (alg); +} + +static int +cmp_dtype(const void *ap, const void *bp) { + int a = *(const uint8_t *)ap; + int b = *(const uint8_t *)bp; + return (a - b); +} + +void +add_dtype(unsigned int dt) { + unsigned i, n; + + /* ensure there is space for a zero terminator */ + n = sizeof(dtype) / sizeof(dtype[0]) - 1; + for (i = 0; i < n; i++) { + if (dtype[i] == dt) { + return; + } + if (dtype[i] == 0) { + dtype[i] = dt; + qsort(dtype, i + 1, 1, cmp_dtype); + return; + } + } + fatal("too many -a digest type arguments"); +} + +isc_result_t +try_dir(const char *dirname) { + isc_result_t result; + isc_dir_t d; + + isc_dir_init(&d); + result = isc_dir_open(&d, dirname); + if (result == ISC_R_SUCCESS) { + isc_dir_close(&d); + } + return (result); +} + +/* + * Check private key version compatibility. + */ +void +check_keyversion(dst_key_t *key, char *keystr) { + int major, minor; + dst_key_getprivateformat(key, &major, &minor); + INSIST(major <= DST_MAJOR_VERSION); /* invalid private key */ + + if (major < DST_MAJOR_VERSION || minor < DST_MINOR_VERSION) { + fatal("Key %s has incompatible format version %d.%d, " + "use -f to force upgrade to new version.", + keystr, major, minor); + } + if (minor > DST_MINOR_VERSION) { + fatal("Key %s has incompatible format version %d.%d, " + "use -f to force downgrade to current version.", + keystr, major, minor); + } +} + +void +set_keyversion(dst_key_t *key) { + int major, minor; + dst_key_getprivateformat(key, &major, &minor); + INSIST(major <= DST_MAJOR_VERSION); + + if (major != DST_MAJOR_VERSION || minor != DST_MINOR_VERSION) { + dst_key_setprivateformat(key, DST_MAJOR_VERSION, + DST_MINOR_VERSION); + } + + /* + * If the key is from a version older than 1.3, set + * set the creation date + */ + if (major < 1 || (major == 1 && minor <= 2)) { + isc_stdtime_t now; + isc_stdtime_get(&now); + dst_key_settime(key, DST_TIME_CREATED, now); + } +} + +bool +key_collision(dst_key_t *dstkey, dns_name_t *name, const char *dir, + isc_mem_t *mctx, bool *exact) { + isc_result_t result; + bool conflict = false; + dns_dnsseckeylist_t matchkeys; + dns_dnsseckey_t *key = NULL; + uint16_t id, oldid; + uint32_t rid, roldid; + dns_secalg_t alg; + char filename[NAME_MAX]; + isc_buffer_t fileb; + isc_stdtime_t now; + + if (exact != NULL) { + *exact = false; + } + + id = dst_key_id(dstkey); + rid = dst_key_rid(dstkey); + alg = dst_key_alg(dstkey); + + /* + * For Diffie Hellman just check if there is a direct collision as + * they can't be revoked. Additionally dns_dnssec_findmatchingkeys + * only handles DNSKEY which is not used for HMAC. + */ + if (alg == DST_ALG_DH) { + isc_buffer_init(&fileb, filename, sizeof(filename)); + result = dst_key_buildfilename(dstkey, DST_TYPE_PRIVATE, dir, + &fileb); + if (result != ISC_R_SUCCESS) { + return (true); + } + return (isc_file_exists(filename)); + } + + ISC_LIST_INIT(matchkeys); + isc_stdtime_get(&now); + result = dns_dnssec_findmatchingkeys(name, dir, now, mctx, &matchkeys); + if (result == ISC_R_NOTFOUND) { + return (false); + } + + while (!ISC_LIST_EMPTY(matchkeys) && !conflict) { + key = ISC_LIST_HEAD(matchkeys); + if (dst_key_alg(key->key) != alg) { + goto next; + } + + oldid = dst_key_id(key->key); + roldid = dst_key_rid(key->key); + + if (oldid == rid || roldid == id || id == oldid) { + conflict = true; + if (id != oldid) { + if (verbose > 1) { + fprintf(stderr, + "Key ID %d could " + "collide with %d\n", + id, oldid); + } + } else { + if (exact != NULL) { + *exact = true; + } + if (verbose > 1) { + fprintf(stderr, "Key ID %d exists\n", + id); + } + } + } + + next: + ISC_LIST_UNLINK(matchkeys, key, link); + dns_dnsseckey_destroy(mctx, &key); + } + + /* Finish freeing the list */ + while (!ISC_LIST_EMPTY(matchkeys)) { + key = ISC_LIST_HEAD(matchkeys); + ISC_LIST_UNLINK(matchkeys, key, link); + dns_dnsseckey_destroy(mctx, &key); + } + + return (conflict); +} + +bool +isoptarg(const char *arg, char **argv, void (*usage)(void)) { + if (!strcasecmp(isc_commandline_argument, arg)) { + if (argv[isc_commandline_index] == NULL) { + fprintf(stderr, "%s: missing argument -%c %s\n", + program, isc_commandline_option, + isc_commandline_argument); + usage(); + } + isc_commandline_argument = argv[isc_commandline_index]; + /* skip to next argument */ + isc_commandline_index++; + return (true); + } + return (false); +} diff --git a/bin/dnssec/dnssectool.h b/bin/dnssec/dnssectool.h new file mode 100644 index 0000000..f38bc55 --- /dev/null +++ b/bin/dnssec/dnssectool.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +#include + +#include + +/*! verbosity: set by -v and -q option in each program, defined in dnssectool.c + */ +extern int verbose; +extern bool quiet; + +/*! program name, statically initialized in each program */ +extern const char *program; + +/*! + * List of DS digest types used by dnssec-cds and dnssec-dsfromkey, + * defined in dnssectool.c. Filled in by add_dtype() from -a + * arguments, sorted (so that DS records are in a canonical order) and + * terminated by a zero. The size of the array is an arbitrary limit + * which should be greater than the number of known digest types. + */ +extern uint8_t dtype[8]; + +typedef void(fatalcallback_t)(void); + +noreturn void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +setfatalcallback(fatalcallback_t *callback); + +void +check_result(isc_result_t result, const char *message); + +void +vbprintf(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3); + +noreturn void +version(const char *program); + +void +sig_format(dns_rdata_rrsig_t *sig, char *cp, unsigned int size); +#define SIG_FORMATSIZE \ + (DNS_NAME_FORMATSIZE + DNS_SECALG_FORMATSIZE + sizeof("65535")) + +void +setup_logging(isc_mem_t *mctx, isc_log_t **logp); + +void +cleanup_logging(isc_log_t **logp); + +dns_ttl_t +strtottl(const char *str); + +dst_key_state_t +strtokeystate(const char *str); + +isc_stdtime_t +strtotime(const char *str, int64_t now, int64_t base, bool *setp); + +dns_rdataclass_t +strtoclass(const char *str); + +unsigned int +strtodsdigest(const char *str); + +void +add_dtype(unsigned int dt); + +isc_result_t +try_dir(const char *dirname); + +void +check_keyversion(dst_key_t *key, char *keystr); + +void +set_keyversion(dst_key_t *key); + +bool +key_collision(dst_key_t *key, dns_name_t *name, const char *dir, + isc_mem_t *mctx, bool *exact); + +bool +isoptarg(const char *arg, char **argv, void (*usage)(void)); diff --git a/bin/named/Makefile.am b/bin/named/Makefile.am new file mode 100644 index 0000000..57a023b --- /dev/null +++ b/bin/named/Makefile.am @@ -0,0 +1,123 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + -I$(top_builddir)/include \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) \ + $(LIBISCCC_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBBIND9_CFLAGS) \ + $(OPENSSL_CFLAGS) \ + $(LIBCAP_CFLAGS) \ + $(LMDB_CFLAGS) \ + $(MAXMINDDB_CFLAGS) \ + $(DNSTAP_CFLAGS) \ + $(LIBUV_CFLAGS) \ + $(ZLIB_CFLAGS) + +if HAVE_JSON_C +AM_CPPFLAGS += \ + $(JSON_C_CFLAGS) +endif HAVE_JSON_C + +if HAVE_LIBNGHTTP2 +AM_CPPFLAGS += \ + $(LIBNGHTTP2_CFLAGS) +endif HAVE_LIBNGHTTP2 + +if HAVE_LIBXML2 +AM_CPPFLAGS += \ + $(LIBXML2_CFLAGS) +endif HAVE_LIBXML2 + +AM_CPPFLAGS += \ + -DNAMED_LOCALSTATEDIR=\"${localstatedir}\" \ + -DNAMED_SYSCONFDIR=\"${sysconfdir}\" + +sbin_PROGRAMS = named + +nodist_named_SOURCES = xsl.c +BUILT_SOURCES = xsl.c +CLEANFILES = xsl.c + +EXTRA_DIST = bind9.xsl + +xsl.c: bind9.xsl Makefile + (echo 'const char xslmsg[] =' && \ + $(SED) -e 's,\",\\\",g' \ + -e 's,^,\",' \ + -e 's,$$,\\n\",' && \ + echo ";") \ + < "${srcdir}/bind9.xsl" > $@ + +named_SOURCES = \ + builtin.c \ + config.c \ + control.c \ + controlconf.c \ + dlz_dlopen_driver.c \ + fuzz.c \ + log.c \ + logconf.c \ + main.c \ + os.c \ + server.c \ + statschannel.c \ + tkeyconf.c \ + transportconf.c \ + tsigconf.c \ + zoneconf.c \ + include/dlz/dlz_dlopen_driver.h \ + include/named/builtin.h \ + include/named/config.h \ + include/named/control.h \ + include/named/fuzz.h \ + include/named/geoip.h \ + include/named/globals.h \ + include/named/log.h \ + include/named/logconf.h \ + include/named/main.h \ + include/named/os.h \ + include/named/server.h \ + include/named/smf_globals.h \ + include/named/statschannel.h \ + include/named/tkeyconf.h \ + include/named/transportconf.h \ + include/named/tsigconf.h \ + include/named/types.h \ + include/named/zoneconf.h \ + xsl_p.h + +if HAVE_GEOIP2 +AM_CPPFLAGS += \ + -DMAXMINDDB_PREFIX=\"@MAXMINDDB_PREFIX@\" +named_SOURCES += \ + geoip.c +endif + +named_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBNS_LIBS) \ + $(LIBISCCC_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) \ + $(OPENSSL_LIBS) \ + $(LIBCAP_LIBS) \ + $(LMDB_LIBS) \ + $(MAXMINDDB_LIBS) \ + $(DNSTAP_LIBS) \ + $(LIBUV_LIBS) \ + $(LIBXML2_LIBS) \ + $(ZLIB_LIBS) + +if HAVE_JSON_C +named_LDADD += \ + $(JSON_C_LIBS) +endif HAVE_JSON_C + +if HAVE_LIBNGHTTP2 +named_LDADD += \ + $(LIBNGHTTP2_LIBS) +endif HAVE_LIBNGHTTP2 diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in new file mode 100644 index 0000000..a38ce3b --- /dev/null +++ b/bin/named/Makefile.in @@ -0,0 +1,964 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +@HAVE_JSON_C_TRUE@am__append_2 = \ +@HAVE_JSON_C_TRUE@ $(JSON_C_CFLAGS) + +@HAVE_LIBNGHTTP2_TRUE@am__append_3 = \ +@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_CFLAGS) + +@HAVE_LIBXML2_TRUE@am__append_4 = \ +@HAVE_LIBXML2_TRUE@ $(LIBXML2_CFLAGS) + +sbin_PROGRAMS = named$(EXEEXT) +@HAVE_GEOIP2_TRUE@am__append_5 = \ +@HAVE_GEOIP2_TRUE@ -DMAXMINDDB_PREFIX=\"@MAXMINDDB_PREFIX@\" + +@HAVE_GEOIP2_TRUE@am__append_6 = \ +@HAVE_GEOIP2_TRUE@ geoip.c + +@HAVE_JSON_C_TRUE@am__append_7 = \ +@HAVE_JSON_C_TRUE@ $(JSON_C_LIBS) + +@HAVE_LIBNGHTTP2_TRUE@am__append_8 = \ +@HAVE_LIBNGHTTP2_TRUE@ $(LIBNGHTTP2_LIBS) + +subdir = bin/named +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am__named_SOURCES_DIST = builtin.c config.c control.c controlconf.c \ + dlz_dlopen_driver.c fuzz.c log.c logconf.c main.c os.c \ + server.c statschannel.c tkeyconf.c transportconf.c tsigconf.c \ + zoneconf.c include/dlz/dlz_dlopen_driver.h \ + include/named/builtin.h include/named/config.h \ + include/named/control.h include/named/fuzz.h \ + include/named/geoip.h include/named/globals.h \ + include/named/log.h include/named/logconf.h \ + include/named/main.h include/named/os.h include/named/server.h \ + include/named/smf_globals.h include/named/statschannel.h \ + include/named/tkeyconf.h include/named/transportconf.h \ + include/named/tsigconf.h include/named/types.h \ + include/named/zoneconf.h xsl_p.h geoip.c +@HAVE_GEOIP2_TRUE@am__objects_1 = geoip.$(OBJEXT) +am_named_OBJECTS = builtin.$(OBJEXT) config.$(OBJEXT) \ + control.$(OBJEXT) controlconf.$(OBJEXT) \ + dlz_dlopen_driver.$(OBJEXT) fuzz.$(OBJEXT) log.$(OBJEXT) \ + logconf.$(OBJEXT) main.$(OBJEXT) os.$(OBJEXT) server.$(OBJEXT) \ + statschannel.$(OBJEXT) tkeyconf.$(OBJEXT) \ + transportconf.$(OBJEXT) tsigconf.$(OBJEXT) zoneconf.$(OBJEXT) \ + $(am__objects_1) +nodist_named_OBJECTS = xsl.$(OBJEXT) +named_OBJECTS = $(am_named_OBJECTS) $(nodist_named_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_JSON_C_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +@HAVE_LIBNGHTTP2_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +named_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBNS_LIBS) \ + $(LIBISCCC_LIBS) $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/builtin.Po ./$(DEPDIR)/config.Po \ + ./$(DEPDIR)/control.Po ./$(DEPDIR)/controlconf.Po \ + ./$(DEPDIR)/dlz_dlopen_driver.Po ./$(DEPDIR)/fuzz.Po \ + ./$(DEPDIR)/geoip.Po ./$(DEPDIR)/log.Po ./$(DEPDIR)/logconf.Po \ + ./$(DEPDIR)/main.Po ./$(DEPDIR)/os.Po ./$(DEPDIR)/server.Po \ + ./$(DEPDIR)/statschannel.Po ./$(DEPDIR)/tkeyconf.Po \ + ./$(DEPDIR)/transportconf.Po ./$(DEPDIR)/tsigconf.Po \ + ./$(DEPDIR)/xsl.Po ./$(DEPDIR)/zoneconf.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(named_SOURCES) $(nodist_named_SOURCES) +DIST_SOURCES = $(am__named_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include -I$(top_builddir)/include $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) $(LIBNS_CFLAGS) $(LIBISCCC_CFLAGS) \ + $(LIBISCCFG_CFLAGS) $(LIBBIND9_CFLAGS) $(OPENSSL_CFLAGS) \ + $(LIBCAP_CFLAGS) $(LMDB_CFLAGS) $(MAXMINDDB_CFLAGS) \ + $(DNSTAP_CFLAGS) $(LIBUV_CFLAGS) $(ZLIB_CFLAGS) \ + $(am__append_2) $(am__append_3) $(am__append_4) \ + -DNAMED_LOCALSTATEDIR=\"${localstatedir}\" \ + -DNAMED_SYSCONFDIR=\"${sysconfdir}\" $(am__append_5) +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +nodist_named_SOURCES = xsl.c +BUILT_SOURCES = xsl.c +CLEANFILES = xsl.c +EXTRA_DIST = bind9.xsl +named_SOURCES = builtin.c config.c control.c controlconf.c \ + dlz_dlopen_driver.c fuzz.c log.c logconf.c main.c os.c \ + server.c statschannel.c tkeyconf.c transportconf.c tsigconf.c \ + zoneconf.c include/dlz/dlz_dlopen_driver.h \ + include/named/builtin.h include/named/config.h \ + include/named/control.h include/named/fuzz.h \ + include/named/geoip.h include/named/globals.h \ + include/named/log.h include/named/logconf.h \ + include/named/main.h include/named/os.h include/named/server.h \ + include/named/smf_globals.h include/named/statschannel.h \ + include/named/tkeyconf.h include/named/transportconf.h \ + include/named/tsigconf.h include/named/types.h \ + include/named/zoneconf.h xsl_p.h $(am__append_6) +named_LDADD = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBNS_LIBS) \ + $(LIBISCCC_LIBS) $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) \ + $(OPENSSL_LIBS) $(LIBCAP_LIBS) $(LMDB_LIBS) $(MAXMINDDB_LIBS) \ + $(DNSTAP_LIBS) $(LIBUV_LIBS) $(LIBXML2_LIBS) $(ZLIB_LIBS) \ + $(am__append_7) $(am__append_8) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/named/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/named/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +named$(EXEEXT): $(named_OBJECTS) $(named_DEPENDENCIES) $(EXTRA_named_DEPENDENCIES) + @rm -f named$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(named_OBJECTS) $(named_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/builtin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/controlconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlz_dlopen_driver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/geoip.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statschannel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tkeyconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/transportconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsigconf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xsl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zoneconf.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/builtin.Po + -rm -f ./$(DEPDIR)/config.Po + -rm -f ./$(DEPDIR)/control.Po + -rm -f ./$(DEPDIR)/controlconf.Po + -rm -f ./$(DEPDIR)/dlz_dlopen_driver.Po + -rm -f ./$(DEPDIR)/fuzz.Po + -rm -f ./$(DEPDIR)/geoip.Po + -rm -f ./$(DEPDIR)/log.Po + -rm -f ./$(DEPDIR)/logconf.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/os.Po + -rm -f ./$(DEPDIR)/server.Po + -rm -f ./$(DEPDIR)/statschannel.Po + -rm -f ./$(DEPDIR)/tkeyconf.Po + -rm -f ./$(DEPDIR)/transportconf.Po + -rm -f ./$(DEPDIR)/tsigconf.Po + -rm -f ./$(DEPDIR)/xsl.Po + -rm -f ./$(DEPDIR)/zoneconf.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/builtin.Po + -rm -f ./$(DEPDIR)/config.Po + -rm -f ./$(DEPDIR)/control.Po + -rm -f ./$(DEPDIR)/controlconf.Po + -rm -f ./$(DEPDIR)/dlz_dlopen_driver.Po + -rm -f ./$(DEPDIR)/fuzz.Po + -rm -f ./$(DEPDIR)/geoip.Po + -rm -f ./$(DEPDIR)/log.Po + -rm -f ./$(DEPDIR)/logconf.Po + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/os.Po + -rm -f ./$(DEPDIR)/server.Po + -rm -f ./$(DEPDIR)/statschannel.Po + -rm -f ./$(DEPDIR)/tkeyconf.Po + -rm -f ./$(DEPDIR)/transportconf.Po + -rm -f ./$(DEPDIR)/tsigconf.Po + -rm -f ./$(DEPDIR)/xsl.Po + -rm -f ./$(DEPDIR)/zoneconf.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-sbinPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir doc-am doc-local dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-sbinPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +xsl.c: bind9.xsl Makefile + (echo 'const char xslmsg[] =' && \ + $(SED) -e 's,\",\\\",g' \ + -e 's,^,\",' \ + -e 's,$$,\\n\",' && \ + echo ";") \ + < "${srcdir}/bind9.xsl" > $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl new file mode 100644 index 0000000..9dda61d --- /dev/null +++ b/bin/named/bind9.xsl @@ -0,0 +1,1106 @@ + + + + + + + + + + + + + + + + + + + ISC BIND 9 Statistics + + +
+

ISC Bind 9 Configuration and Statistics

+
+

Alternate statistics views: All, + Status, + Server, + Zones, + Network, + Tasks, + Memory and + Traffic Size

+
+

Server Status

+ + + + + + + + + + + + + + + + + +
Boot time: + +
Last reconfigured: + +
Current time: + +
Server version: + +
+
+ + +

Incoming Requests by DNS Opcode

+ +
+ [cannot display chart] +
+
+ + + + + + even + odd + + + + + + + + + + + +
+ + + +
Total: + +
+
+
+ + + +

Incoming Queries by Query Type

+
+ [cannot display chart] +
+
+ + + + + + even + odd + + + + + + + + + + + +
+ + + +
Total: + +
+
+
+ +

Outgoing Queries per view

+ +

View

+ + + + + + +
[no data to display]
+
+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+
+ +

Server Statistics

+ + + +
[no data to display]
+
+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+ + +

Zone Maintenance Statistics

+ + +
[no data to display]
+
+ + + + + + even + odd + + + + + + + +
+ + + +
+
+ +

Resolver Statistics (Common)

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+ + +

Resolver Statistics for View

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+ + +

ADB Statistics for View

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+ + +

Cache Statistics for View

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+ + +

Cache DB RRsets for View

+ + + + + even + odd + + + + + + + +
+ + + +
+
+
+
+ +

Traffic Size Statistics

+
+ +

UDP Requests Received

+ + + + + even + odd + + + + + + + + +
+ + + +
+
+
+ +

UDP Responses Sent

+ + + + + even + odd + + + + + + + + +
+ + + +
+
+
+ +

TCP Requests Received

+ + + + + even + odd + + + + + + + + +
+ + + +
+
+
+ +

TCP Responses Sent

+ + + + + even + odd + + + + + + + + +
+ + + +
+
+
+ +

Socket I/O Statistics

+ + + + + even + odd + + + + + + + +
+ + + +
+
+
+ + +

Zones for View

+ + + + + + + even + odd + + + + + + + + + + + + +
NameClassTypeSerialLoadedExpiresRefresh
+
+
+ +

Received QTYPES per view/zone

+ +

View

+ + + + + +

Zone

+ + + + + + +
[no data to display]
+
+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+
+
+ +

Response Codes per view/zone

+ +

View

+ + + + + +

Zone

+ + + + + + +
[no data to display]
+
+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+
+
+ +

Glue cache statistics

+ +

View

+ + + + + +

Zone

+ + + + + + even + odd + + + + + + + +
+ + + +
+
+
+
+
+ +

Task Manager Configuration

+ + + + + + + + + + + + + + + + + + + + + +
Thread-Model + +
Worker Threads + +
Default Quantum + +
Tasks Running + +
Tasks Ready + +
+
+
+ +

Tasks

+ + + + + + + + + + + + + + even + odd + + + + + + + + + + + +
IDNameReferencesStateQuantumEvents
+ + + + + + + + + + + +
+
+
+ +

Memory Usage Summary

+ + + + + even + odd + + + + + + + +
+ + + +
+
+
+ +

Memory Contexts

+ + + + + + + + + + + + + + + + + + + + even + odd + + + + + + + + + + + + + + + + + +
IDNameReferencesTotalUseInUseMaxUseMallocedMaxMallocedBlockSizePoolsHiWaterLoWater
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+
diff --git a/bin/named/builtin.c b/bin/named/builtin.c new file mode 100644 index 0000000..26348fd --- /dev/null +++ b/bin/named/builtin.c @@ -0,0 +1,650 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file + * \brief + * The built-in "version", "hostname", "id", "authors" and "empty" databases. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +typedef struct builtin builtin_t; + +static isc_result_t +do_authors_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_dns64_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_empty_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_hostname_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_id_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_ipv4only_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_ipv4reverse_lookup(dns_sdblookup_t *lookup); +static isc_result_t +do_version_lookup(dns_sdblookup_t *lookup); + +/* + * We can't use function pointers as the db_data directly + * because ANSI C does not guarantee that function pointers + * can safely be cast to void pointers and back. + */ + +struct builtin { + isc_result_t (*do_lookup)(dns_sdblookup_t *lookup); + char *server; + char *contact; +}; + +static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL }; +static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL }; +static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL }; +static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL }; +static builtin_t id_builtin = { do_id_lookup, NULL, NULL }; +static builtin_t ipv4only_builtin = { do_ipv4only_lookup, NULL, NULL }; +static builtin_t ipv4reverse_builtin = { do_ipv4reverse_lookup, NULL, NULL }; +static builtin_t version_builtin = { do_version_lookup, NULL, NULL }; + +static dns_sdbimplementation_t *builtin_impl; +static dns_sdbimplementation_t *dns64_impl; + +/* + * Pre computed HEX * 16 or 1 table. + */ +static const unsigned char hex16[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*00*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*20*/ + 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 1, 1, 1, 1, 1, 1, /*30*/ + 1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*40*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*50*/ + 1, 160, 176, 192, 208, 224, 240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*60*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*70*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*80*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*90*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*A0*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*B0*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*C0*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*D0*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*E0*/ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /*F0*/ +}; + +static const unsigned char decimal[] = "0123456789"; +static const unsigned char ipv4only[] = "\010ipv4only\004arpa"; + +static size_t +dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) { + size_t i, j = 0; + + for (i = 0; i < 4U; i++) { + unsigned char c = v[start++]; + if (start == 7U) { + start++; + } + if (c > 99) { + rdata[j++] = 3; + rdata[j++] = decimal[c / 100]; + c = c % 100; + rdata[j++] = decimal[c / 10]; + c = c % 10; + rdata[j++] = decimal[c]; + } else if (c > 9) { + rdata[j++] = 2; + rdata[j++] = decimal[c / 10]; + c = c % 10; + rdata[j++] = decimal[c]; + } else { + rdata[j++] = 1; + rdata[j++] = decimal[c]; + } + } + memmove(&rdata[j], "\07in-addr\04arpa", 14); + return (j + 14); +} + +static isc_result_t +dns64_cname(const dns_name_t *zone, const dns_name_t *name, + dns_sdblookup_t *lookup) { + size_t zlen, nlen, j, len; + unsigned char v[16], n; + unsigned int i; + unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")]; + unsigned char *ndata; + + /* + * The combined length of the zone and name is 74. + * + * The minimum zone length is 10 ((3)ip6(4)arpa(0)). + * + * The length of name should always be even as we are expecting + * a series of nibbles. + */ + zlen = zone->length; + nlen = name->length; + if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U) { + return (ISC_R_NOTFOUND); + } + + /* + * We assume the zone name is well formed. + */ + + /* + * XXXMPA We could check the dns64 suffix here if we need to. + */ + /* + * Check that name is a series of nibbles. + * Compute the byte values that correspond to the nibbles as we go. + * + * Shift the final result 4 bits, by setting 'i' to 1, if we if we + * have a odd number of nibbles so that "must be zero" tests below + * are byte aligned and we correctly return ISC_R_NOTFOUND or + * ISC_R_SUCCESS. We will not generate a CNAME in this case. + */ + ndata = name->ndata; + i = (nlen % 4) == 2U ? 1 : 0; + j = nlen; + memset(v, 0, sizeof(v)); + while (j != 0U) { + INSIST((i / 2) < sizeof(v)); + if (ndata[0] != 1) { + return (ISC_R_NOTFOUND); + } + n = hex16[ndata[1] & 0xff]; + if (n == 1) { + return (ISC_R_NOTFOUND); + } + v[i / 2] = n | (v[i / 2] >> 4); + j -= 2; + ndata += 2; + i++; + } + + /* + * If we get here then we know name only consisted of nibbles. + * Now we need to determine if the name exists or not and whether + * it corresponds to a empty node in the zone or there should be + * a CNAME. + */ +#define ZLEN(x) (10 + (x) / 2) + switch (zlen) { + case ZLEN(32): /* prefix len 32 */ + /* + * The nibbles that map to this byte must be zero for 'name' + * to exist in the zone. + */ + if (nlen > 16U && v[(nlen - 1) / 4 - 4] != 0) { + return (ISC_R_NOTFOUND); + } + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 8, rdata); + break; + case ZLEN(40): /* prefix len 40 */ + /* + * The nibbles that map to this byte must be zero for 'name' + * to exist in the zone. + */ + if (nlen > 12U && v[(nlen - 1) / 4 - 3] != 0) { + return (ISC_R_NOTFOUND); + } + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 6, rdata); + break; + case ZLEN(48): /* prefix len 48 */ + /* + * The nibbles that map to this byte must be zero for 'name' + * to exist in the zone. + */ + if (nlen > 8U && v[(nlen - 1) / 4 - 2] != 0) { + return (ISC_R_NOTFOUND); + } + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 5, rdata); + break; + case ZLEN(56): /* prefix len 56 */ + /* + * The nibbles that map to this byte must be zero for 'name' + * to exist in the zone. + */ + if (nlen > 4U && v[(nlen - 1) / 4 - 1] != 0) { + return (ISC_R_NOTFOUND); + } + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 4, rdata); + break; + case ZLEN(64): /* prefix len 64 */ + /* + * The nibbles that map to this byte must be zero for 'name' + * to exist in the zone. + */ + if (v[(nlen - 1) / 4] != 0) { + return (ISC_R_NOTFOUND); + } + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 3, rdata); + break; + case ZLEN(96): /* prefix len 96 */ + /* + * If the total length is not 74 then this is a empty node + * so return success. + */ + if (nlen + zlen != 74U) { + return (ISC_R_SUCCESS); + } + len = dns64_rdata(v, 0, rdata); + break; + default: + /* + * This should never be reached unless someone adds a + * zone declaration with this internal type to named.conf. + */ + return (ISC_R_NOTFOUND); + } + + /* + * Reverse of 192.0.0.170 or 192.0.0.171 maps to ipv4only.arpa. + */ + if ((v[0] == 170 || v[0] == 171) && v[1] == 0 && v[2] == 0 && + v[3] == 192) + { + return (dns_sdb_putrdata(lookup, dns_rdatatype_ptr, 3600, + ipv4only, sizeof(ipv4only))); + } + + return (dns_sdb_putrdata(lookup, dns_rdatatype_cname, 600, rdata, + (unsigned int)len)); +} + +static isc_result_t +builtin_lookup(const char *zone, const char *name, void *dbdata, + dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + builtin_t *b = (builtin_t *)dbdata; + + UNUSED(zone); + UNUSED(methods); + UNUSED(clientinfo); + + if (strcmp(name, "@") == 0) { + return (b->do_lookup(lookup)); + } else { + return (ISC_R_NOTFOUND); + } +} + +static isc_result_t +dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata, + dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + builtin_t *b = (builtin_t *)dbdata; + + UNUSED(methods); + UNUSED(clientinfo); + + if (name->labels == 0 && name->length == 0) { + return (b->do_lookup(lookup)); + } else { + return (dns64_cname(zone, name, lookup)); + } +} + +static isc_result_t +put_txt(dns_sdblookup_t *lookup, const char *text) { + unsigned char buf[256]; + unsigned int len = strlen(text); + if (len > 255) { + len = 255; /* Silently truncate */ + } + buf[0] = len; + memmove(&buf[1], text, len); + return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1)); +} + +static isc_result_t +do_version_lookup(dns_sdblookup_t *lookup) { + if (named_g_server->version_set) { + if (named_g_server->version == NULL) { + return (ISC_R_SUCCESS); + } else { + return (put_txt(lookup, named_g_server->version)); + } + } else { + return (put_txt(lookup, PACKAGE_VERSION)); + } +} + +static isc_result_t +do_hostname_lookup(dns_sdblookup_t *lookup) { + if (named_g_server->hostname_set) { + if (named_g_server->hostname == NULL) { + return (ISC_R_SUCCESS); + } else { + return (put_txt(lookup, named_g_server->hostname)); + } + } else { + char buf[256]; + if (gethostname(buf, sizeof(buf)) != 0) { + return (ISC_R_FAILURE); + } + return (put_txt(lookup, buf)); + } +} + +static isc_result_t +do_authors_lookup(dns_sdblookup_t *lookup) { + isc_result_t result; + const char **p; + static const char *authors[] = { + "Mark Andrews", "Curtis Blackburn", "James Brister", + "Ben Cottrell", "John H. DuBois III", "Francis Dupont", + "Michael Graff", "Andreas Gustafsson", "Bob Halley", + "Evan Hunt", "JINMEI Tatuya", "Witold Krecicki", + "David Lawrence", "Scott Mann", "Danny Mayer", + "Damien Neil", "Matt Nelson", "Jeremy C. Reed", + "Michael Sawyer", "Brian Wellington", NULL + }; + + /* + * If a version string is specified, disable the authors.bind zone. + */ + if (named_g_server->version_set) { + return (ISC_R_SUCCESS); + } + + for (p = authors; *p != NULL; p++) { + result = put_txt(lookup, *p); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +do_id_lookup(dns_sdblookup_t *lookup) { + if (named_g_server->sctx->usehostname) { + char buf[256]; + if (gethostname(buf, sizeof(buf)) != 0) { + return (ISC_R_FAILURE); + } + return (put_txt(lookup, buf)); + } else if (named_g_server->sctx->server_id != NULL) { + return (put_txt(lookup, named_g_server->sctx->server_id)); + } else { + return (ISC_R_SUCCESS); + } +} + +static isc_result_t +do_dns64_lookup(dns_sdblookup_t *lookup) { + UNUSED(lookup); + return (ISC_R_SUCCESS); +} + +static isc_result_t +do_empty_lookup(dns_sdblookup_t *lookup) { + UNUSED(lookup); + return (ISC_R_SUCCESS); +} + +static isc_result_t +do_ipv4only_lookup(dns_sdblookup_t *lookup) { + isc_result_t result; + unsigned char data[2][4] = { { 192, 0, 0, 170 }, { 192, 0, 0, 171 } }; + + for (int i = 0; i < 2; i++) { + result = dns_sdb_putrdata(lookup, dns_rdatatype_a, 3600, + data[i], 4); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +do_ipv4reverse_lookup(dns_sdblookup_t *lookup) { + isc_result_t result; + + result = dns_sdb_putrdata(lookup, dns_rdatatype_ptr, 3600, ipv4only, + sizeof(ipv4only)); + return (result); +} + +static isc_result_t +builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) { + isc_result_t result; + const char *contact = "hostmaster"; + const char *server = "@"; + builtin_t *b = (builtin_t *)dbdata; + + UNUSED(zone); + UNUSED(dbdata); + + if (b == &empty_builtin) { + server = "."; + contact = "."; + } else { + if (b->server != NULL) { + server = b->server; + } + if (b->contact != NULL) { + contact = b->contact; + } + } + + result = dns_sdb_putsoa(lookup, server, contact, 0); + if (result != ISC_R_SUCCESS) { + return (ISC_R_FAILURE); + } + + result = dns_sdb_putrr(lookup, "ns", 0, server); + if (result != ISC_R_SUCCESS) { + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +builtin_create(const char *zone, int argc, char **argv, void *driverdata, + void **dbdata) { + REQUIRE(argc >= 1); + + UNUSED(zone); + UNUSED(driverdata); + + if (strcmp(argv[0], "dns64") == 0 || strcmp(argv[0], "empty") == 0 || + strcmp(argv[0], "ipv4only") == 0 || + strcmp(argv[0], "ipv4reverse") == 0) + { + if (argc != 3) { + return (DNS_R_SYNTAX); + } + } else if (argc != 1) { + return (DNS_R_SYNTAX); + } + + if (strcmp(argv[0], "authors") == 0) { + *dbdata = &authors_builtin; + } else if (strcmp(argv[0], "hostname") == 0) { + *dbdata = &hostname_builtin; + } else if (strcmp(argv[0], "id") == 0) { + *dbdata = &id_builtin; + } else if (strcmp(argv[0], "version") == 0) { + *dbdata = &version_builtin; + } else if (strcmp(argv[0], "dns64") == 0 || + strcmp(argv[0], "empty") == 0 || + strcmp(argv[0], "ipv4only") == 0 || + strcmp(argv[0], "ipv4reverse") == 0) + { + builtin_t *empty; + char *server; + char *contact; + + if (argc != 3) { + return (DNS_R_SYNTAX); + } + + /* + * We don't want built-in zones to fail. Fallback to + * the static configuration if memory allocation fails. + */ + empty = isc_mem_get(named_g_mctx, sizeof(*empty)); + server = isc_mem_strdup(named_g_mctx, argv[1]); + contact = isc_mem_strdup(named_g_mctx, argv[2]); + if (empty == NULL || server == NULL || contact == NULL) { + if (strcmp(argv[0], "dns64") == 0) { + *dbdata = &dns64_builtin; + } else if (strcmp(argv[0], "empty") == 0) { + *dbdata = &empty_builtin; + } else if (strcmp(argv[0], "ipv4only") == 0) { + *dbdata = &ipv4only_builtin; + } else { + *dbdata = &ipv4reverse_builtin; + } + if (server != NULL) { + isc_mem_free(named_g_mctx, server); + } + if (contact != NULL) { + isc_mem_free(named_g_mctx, contact); + } + if (empty != NULL) { + isc_mem_put(named_g_mctx, empty, + sizeof(*empty)); + } + } else { + if (strcmp(argv[0], "dns64") == 0) { + memmove(empty, &dns64_builtin, + sizeof(empty_builtin)); + } else if (strcmp(argv[0], "empty") == 0) { + memmove(empty, &empty_builtin, + sizeof(empty_builtin)); + } else if (strcmp(argv[0], "ipv4only") == 0) { + memmove(empty, &ipv4only_builtin, + sizeof(empty_builtin)); + } else { + memmove(empty, &ipv4reverse_builtin, + sizeof(empty_builtin)); + } + empty->server = server; + empty->contact = contact; + *dbdata = empty; + } + } else { + return (ISC_R_NOTIMPLEMENTED); + } + return (ISC_R_SUCCESS); +} + +static void +builtin_destroy(const char *zone, void *driverdata, void **dbdata) { + builtin_t *b = (builtin_t *)*dbdata; + + UNUSED(zone); + UNUSED(driverdata); + + /* + * Don't free the static versions. + */ + if (*dbdata == &authors_builtin || *dbdata == &dns64_builtin || + *dbdata == &empty_builtin || *dbdata == &hostname_builtin || + *dbdata == &id_builtin || *dbdata == &ipv4only_builtin || + *dbdata == &ipv4reverse_builtin || *dbdata == &version_builtin) + { + return; + } + + isc_mem_free(named_g_mctx, b->server); + isc_mem_free(named_g_mctx, b->contact); + isc_mem_put(named_g_mctx, b, sizeof(*b)); +} + +static dns_sdbmethods_t builtin_methods = { + builtin_lookup, builtin_authority, NULL, /* allnodes */ + builtin_create, builtin_destroy, NULL +}; + +static dns_sdbmethods_t dns64_methods = { + NULL, builtin_authority, NULL, /* allnodes */ + builtin_create, builtin_destroy, dns64_lookup, +}; + +isc_result_t +named_builtin_init(void) { + RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL, + DNS_SDBFLAG_RELATIVEOWNER | + DNS_SDBFLAG_RELATIVERDATA, + named_g_mctx, + &builtin_impl) == ISC_R_SUCCESS); + RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL, + DNS_SDBFLAG_RELATIVEOWNER | + DNS_SDBFLAG_RELATIVERDATA | + DNS_SDBFLAG_DNS64, + named_g_mctx, + &dns64_impl) == ISC_R_SUCCESS); + return (ISC_R_SUCCESS); +} + +void +named_builtin_deinit(void) { + dns_sdb_unregister(&builtin_impl); + dns_sdb_unregister(&dns64_impl); +} diff --git a/bin/named/config.c b/bin/named/config.c new file mode 100644 index 0000000..7f318a2 --- /dev/null +++ b/bin/named/config.c @@ -0,0 +1,1034 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +/*% default configuration */ +static char defaultconf[] = "\ +options {\n\ + answer-cookie true;\n\ + automatic-interface-scan yes;\n\ + bindkeys-file \"" NAMED_SYSCONFDIR "/bind.keys\";\n\ +# blackhole {none;};\n" + " cookie-algorithm siphash24;\n" + " coresize default;\n\ + datasize default;\n" + "\ +# directory \n\ + dnssec-policy \"none\";\n\ + dump-file \"named_dump.db\";\n\ + edns-udp-size 1232;\n\ + files unlimited;\n" +#if defined(HAVE_GEOIP2) + "\ + geoip-directory \"" MAXMINDDB_PREFIX "/share/GeoIP\";\n" +#elif defined(HAVE_GEOIP2) + "\ + geoip-directory \".\";\n" +#endif /* if defined(HAVE_GEOIP2) */ + "\ + heartbeat-interval 60;\n\ + interface-interval 60;\n\ +# keep-response-order {none;};\n\ + listen-on {any;};\n\ + listen-on-v6 {any;};\n\ +# lock-file \"" NAMED_LOCALSTATEDIR "/run/named/named.lock\";\n\ + match-mapped-addresses no;\n\ + max-ixfr-ratio 100%;\n\ + max-rsa-exponent-size 0; /* no limit */\n\ + max-udp-size 1232;\n\ + memstatistics-file \"named.memstats\";\n\ + nocookie-udp-size 4096;\n\ + notify-rate 20;\n\ + nta-lifetime 3600;\n\ + nta-recheck 300;\n\ +# pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\ + port 53;\n" +#if HAVE_SO_REUSEPORT_LB + "\ + reuseport yes;\n" +#else + "\ + reuseport no;\n" +#endif + "\ + tls-port 853;\n" +#if HAVE_LIBNGHTTP2 + "\ + http-port 80;\n\ + https-port 443;\n\ + http-listener-clients 300;\n\ + http-streams-per-connection 100;\n" +#endif + "\ + prefetch 2 9;\n\ + recursing-file \"named.recursing\";\n\ + recursive-clients 1000;\n\ + request-nsid false;\n\ + reserved-sockets 512;\n\ + resolver-query-timeout 10;\n\ + rrset-order { order random; };\n\ + secroots-file \"named.secroots\";\n\ + send-cookie true;\n\ + serial-query-rate 20;\n\ + server-id none;\n\ + session-keyalg hmac-sha256;\n\ +# session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\ + session-keyname local-ddns;\n\ + stacksize default;\n\ + startup-notify-rate 20;\n\ + statistics-file \"named.stats\";\n\ + tcp-advertised-timeout 300;\n\ + tcp-clients 150;\n\ + tcp-idle-timeout 300;\n\ + tcp-initial-timeout 300;\n\ + tcp-keepalive-timeout 300;\n\ + tcp-listen-queue 10;\n\ + tcp-receive-buffer 0;\n\ + tcp-send-buffer 0;\n\ +# tkey-dhkey \n\ +# tkey-domain \n\ +# tkey-gssapi-credential \n\ + transfer-message-size 20480;\n\ + transfers-in 10;\n\ + transfers-out 10;\n\ + transfers-per-ns 2;\n\ + trust-anchor-telemetry yes;\n\ + udp-receive-buffer 0;\n\ + udp-send-buffer 0;\n\ + update-quota 100;\n\ +\n\ + /* view */\n\ + allow-new-zones no;\n\ + allow-notify {none;};\n\ + allow-query-cache { localnets; localhost; };\n\ + allow-query-cache-on { any; };\n\ + allow-recursion { localnets; localhost; };\n\ + allow-recursion-on { any; };\n\ + allow-update-forwarding {none;};\n\ + auth-nxdomain false;\n\ + check-dup-records warn;\n\ + check-mx warn;\n\ + check-names primary fail;\n\ + check-names response ignore;\n\ + check-names secondary warn;\n\ + check-spf warn;\n\ + clients-per-query 10;\n\ + dnssec-accept-expired no;\n\ + dnssec-validation " VALIDATION_DEFAULT "; \n" +#ifdef HAVE_DNSTAP + " dnstap-identity hostname;\n" +#endif /* ifdef HAVE_DNSTAP */ + "\ + fetch-quota-params 100 0.1 0.3 0.7;\n\ + fetches-per-server 0;\n\ + fetches-per-zone 0;\n\ + glue-cache yes;\n\ + lame-ttl 0;\n" +#ifdef HAVE_LMDB + " lmdb-mapsize 32M;\n" +#endif /* ifdef HAVE_LMDB */ + " max-cache-size 90%;\n\ + max-cache-ttl 604800; /* 1 week */\n\ + max-clients-per-query 100;\n\ + max-ncache-ttl 10800; /* 3 hours */\n\ + max-recursion-depth 7;\n\ + max-recursion-queries 100;\n\ + max-stale-ttl 86400; /* 1 day */\n\ + message-compression yes;\n\ + min-ncache-ttl 0; /* 0 hours */\n\ + min-cache-ttl 0; /* 0 seconds */\n\ + minimal-any false;\n\ + minimal-responses no-auth-recursive;\n\ + notify-source *;\n\ + notify-source-v6 *;\n\ + nsec3-test-zone no;\n\ + parental-source *;\n\ + parental-source-v6 *;\n\ + provide-ixfr true;\n\ + qname-minimization relaxed;\n\ + query-source address *;\n\ + query-source-v6 address *;\n\ + recursion true;\n\ + request-expire true;\n\ + request-ixfr true;\n\ + require-server-cookie no;\n\ + resolver-nonbackoff-tries 3;\n\ + resolver-retry-interval 800; /* in milliseconds */\n\ + root-key-sentinel yes;\n\ + servfail-ttl 1;\n\ +# sortlist \n\ + stale-answer-client-timeout off;\n\ + stale-answer-enable false;\n\ + stale-answer-ttl 30; /* 30 seconds */\n\ + stale-cache-enable false;\n\ + stale-refresh-time 30; /* 30 seconds */\n\ + synth-from-dnssec yes;\n\ +# topology \n\ + transfer-format many-answers;\n\ + v6-bias 50;\n\ + zero-no-soa-ttl-cache no;\n\ +\n\ + /* zone */\n\ + allow-query {any;};\n\ + allow-query-on {any;};\n\ + allow-transfer {any;};\n\ +# also-notify \n\ + alt-transfer-source *;\n\ + alt-transfer-source-v6 *;\n\ + check-integrity yes;\n\ + check-mx-cname warn;\n\ + check-sibling yes;\n\ + check-srv-cname warn;\n\ + check-wildcard yes;\n\ + dialup no;\n\ + dnssec-dnskey-kskonly yes;\n\ + dnssec-loadkeys-interval 60;\n\ + dnssec-secure-to-insecure no;\n\ + dnssec-update-mode maintain;\n\ +# forward \n\ +# forwarders \n\ +# inline-signing no;\n\ + ixfr-from-differences false;\n\ + max-journal-size default;\n\ + max-records 0;\n\ + max-refresh-time 2419200; /* 4 weeks */\n\ + max-retry-time 1209600; /* 2 weeks */\n\ + max-transfer-idle-in 60;\n\ + max-transfer-idle-out 60;\n\ + max-transfer-time-in 120;\n\ + max-transfer-time-out 120;\n\ + min-refresh-time 300;\n\ + min-retry-time 500;\n\ + multi-master no;\n\ + notify yes;\n\ + notify-delay 5;\n\ + notify-to-soa no;\n\ + serial-update-method increment;\n\ + sig-signing-nodes 100;\n\ + sig-signing-signatures 10;\n\ + sig-signing-type 65534;\n\ + sig-validity-interval 30; /* days */\n\ + dnskey-sig-validity 0; /* default: sig-validity-interval */\n\ + transfer-source *;\n\ + transfer-source-v6 *;\n\ + try-tcp-refresh yes; /* BIND 8 compat */\n\ + update-check-ksk yes;\n\ + zero-no-soa-ttl yes;\n\ + zone-statistics terse;\n\ +};\n\ +" + + "#\n\ +# Zones in the \"_bind\" view are NOT counted in the count of zones.\n\ +#\n\ +view \"_bind\" chaos {\n\ + recursion no;\n\ + notify no;\n\ + allow-new-zones no;\n\ + max-cache-size 2M;\n\ +\n\ + # Prevent use of this zone in DNS amplified reflection DoS attacks\n\ + rate-limit {\n\ + responses-per-second 3;\n\ + slip 0;\n\ + min-table-size 10;\n\ + };\n\ +\n\ + zone \"version.bind\" chaos {\n\ + type primary;\n\ + database \"_builtin version\";\n\ + };\n\ +\n\ + zone \"hostname.bind\" chaos {\n\ + type primary;\n\ + database \"_builtin hostname\";\n\ + };\n\ +\n\ + zone \"authors.bind\" chaos {\n\ + type primary;\n\ + database \"_builtin authors\";\n\ + };\n\ +\n\ + zone \"id.server\" chaos {\n\ + type primary;\n\ + database \"_builtin id\";\n\ + };\n\ +};\n\ +" + "#\n\ +# Built-in DNSSEC key and signing policies.\n\ +#\n\ +dnssec-policy \"default\" {\n\ + keys {\n\ + csk key-directory lifetime unlimited algorithm 13;\n\ + };\n\ +\n\ + dnskey-ttl " DNS_KASP_KEY_TTL ";\n\ + publish-safety " DNS_KASP_PUBLISH_SAFETY "; \n\ + retire-safety " DNS_KASP_RETIRE_SAFETY "; \n\ + purge-keys " DNS_KASP_PURGE_KEYS "; \n\ + signatures-refresh " DNS_KASP_SIG_REFRESH "; \n\ + signatures-validity " DNS_KASP_SIG_VALIDITY "; \n\ + signatures-validity-dnskey " DNS_KASP_SIG_VALIDITY_DNSKEY "; \n\ + max-zone-ttl " DNS_KASP_ZONE_MAXTTL "; \n\ + zone-propagation-delay " DNS_KASP_ZONE_PROPDELAY "; \n\ + parent-ds-ttl " DNS_KASP_DS_TTL "; \n\ + parent-propagation-delay " DNS_KASP_PARENT_PROPDELAY "; \n\ +};\n\ +\n\ +dnssec-policy \"insecure\" {\n\ + max-zone-ttl 0; \n\ + keys { };\n\ +};\n\ +\n\ +" + "#\n\ +# Default trusted key(s), used if \n\ +# \"dnssec-validation auto;\" is set and\n\ +# " NAMED_SYSCONFDIR "/bind.keys doesn't exist).\n\ +#\n\ +# BEGIN TRUST ANCHORS\n" + + /* Imported from bind.keys.h: */ + TRUST_ANCHORS + + "# END TRUST ANCHORS\n\ +\n\ +primaries " DEFAULT_IANA_ROOT_ZONE_PRIMARIES " {\n\ + 2001:500:200::b; # b.root-servers.net\n\ + 2001:500:2::c; # c.root-servers.net\n\ + 2001:500:2f::f; # f.root-servers.net\n\ + 2001:500:12::d0d; # g.root-servers.net\n\ + 2001:7fd::1; # k.root-servers.net\n\ + 2620:0:2830:202::132; # xfr.cjr.dns.icann.org\n\ + 2620:0:2d0:202::132; # xfr.lax.dns.icann.org\n\ + 199.9.14.201; # b.root-servers.net\n\ + 192.33.4.12; # c.root-servers.net\n\ + 192.5.5.241; # f.root-servers.net\n\ + 192.112.36.4; # g.root-servers.net\n\ + 193.0.14.129; # k.root-servers.net\n\ + 192.0.47.132; # xfr.cjr.dns.icann.org\n\ + 192.0.32.132; # xfr.lax.dns.icann.org\n\ +};\n\ +"; + +isc_result_t +named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) { + isc_buffer_t b; + + isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1); + isc_buffer_add(&b, sizeof(defaultconf) - 1); + return (cfg_parse_buffer(parser, &b, __FILE__, 0, &cfg_type_namedconf, + CFG_PCTX_NODEPRECATED, conf)); +} + +const char * +named_config_getdefault(void) { + return (defaultconf); +} + +isc_result_t +named_config_get(cfg_obj_t const *const *maps, const char *name, + const cfg_obj_t **obj) { + int i; + + for (i = 0; maps[i] != NULL; i++) { + if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + } + return (ISC_R_NOTFOUND); +} + +isc_result_t +named_checknames_get(const cfg_obj_t **maps, const char *const names[], + const cfg_obj_t **obj) { + const cfg_listelt_t *element; + const cfg_obj_t *checknames; + const cfg_obj_t *type; + const cfg_obj_t *value; + int i; + + REQUIRE(maps != NULL); + REQUIRE(names != NULL); + REQUIRE(obj != NULL && *obj == NULL); + + for (i = 0; maps[i] != NULL; i++) { + checknames = NULL; + if (cfg_map_get(maps[i], "check-names", &checknames) == + ISC_R_SUCCESS) + { + /* + * Zone map entry is not a list. + */ + if (checknames != NULL && !cfg_obj_islist(checknames)) { + *obj = checknames; + return (ISC_R_SUCCESS); + } + for (element = cfg_list_first(checknames); + element != NULL; element = cfg_list_next(element)) + { + value = cfg_listelt_value(element); + type = cfg_tuple_get(value, "type"); + + for (size_t j = 0; names[j] != NULL; j++) { + if (strcasecmp(cfg_obj_asstring(type), + names[j]) == 0) + { + *obj = cfg_tuple_get(value, + "mode"); + return (ISC_R_SUCCESS); + } + } + } + } + } + return (ISC_R_NOTFOUND); +} + +int +named_config_listcount(const cfg_obj_t *list) { + const cfg_listelt_t *e; + int i = 0; + + for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e)) { + i++; + } + + return (i); +} + +isc_result_t +named_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass, + dns_rdataclass_t *classp) { + isc_textregion_t r; + isc_result_t result; + + if (!cfg_obj_isstring(classobj)) { + *classp = defclass; + return (ISC_R_SUCCESS); + } + DE_CONST(cfg_obj_asstring(classobj), r.base); + r.length = strlen(r.base); + result = dns_rdataclass_fromtext(classp, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(classobj, named_g_lctx, ISC_LOG_ERROR, + "unknown class '%s'", r.base); + } + return (result); +} + +isc_result_t +named_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype, + dns_rdatatype_t *typep) { + isc_textregion_t r; + isc_result_t result; + + if (!cfg_obj_isstring(typeobj)) { + *typep = deftype; + return (ISC_R_SUCCESS); + } + DE_CONST(cfg_obj_asstring(typeobj), r.base); + r.length = strlen(r.base); + result = dns_rdatatype_fromtext(typep, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(typeobj, named_g_lctx, ISC_LOG_ERROR, + "unknown type '%s'", r.base); + } + return (result); +} + +dns_zonetype_t +named_config_getzonetype(const cfg_obj_t *zonetypeobj) { + dns_zonetype_t ztype = dns_zone_none; + const char *str; + + str = cfg_obj_asstring(zonetypeobj); + if (strcasecmp(str, "primary") == 0 || strcasecmp(str, "master") == 0) { + ztype = dns_zone_primary; + } else if (strcasecmp(str, "secondary") == 0 || + strcasecmp(str, "slave") == 0) + { + ztype = dns_zone_secondary; + } else if (strcasecmp(str, "mirror") == 0) { + ztype = dns_zone_mirror; + } else if (strcasecmp(str, "stub") == 0) { + ztype = dns_zone_stub; + } else if (strcasecmp(str, "static-stub") == 0) { + ztype = dns_zone_staticstub; + } else if (strcasecmp(str, "redirect") == 0) { + ztype = dns_zone_redirect; + } else { + UNREACHABLE(); + } + return (ztype); +} + +isc_result_t +named_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list, + in_port_t defport, isc_mem_t *mctx, + isc_sockaddr_t **addrsp, uint32_t *countp) { + int count, i = 0; + const cfg_obj_t *addrlist = NULL; + const cfg_obj_t *portobj = NULL; + const cfg_listelt_t *element = NULL; + isc_sockaddr_t *addrs = NULL; + in_port_t port; + isc_result_t result; + + INSIST(addrsp != NULL && *addrsp == NULL); + INSIST(countp != NULL); + + addrlist = cfg_tuple_get(list, "addresses"); + count = named_config_listcount(addrlist); + + portobj = cfg_tuple_get(list, "port"); + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + return (ISC_R_RANGE); + } + port = (in_port_t)val; + } else if (defport != 0) { + port = defport; + } else { + result = named_config_getport(config, "port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t)); + + for (element = cfg_list_first(addrlist); element != NULL; + element = cfg_list_next(element), i++) + { + const cfg_obj_t *addr; + INSIST(i < count); + addr = cfg_listelt_value(element); + addrs[i] = *cfg_obj_assockaddr(addr); + if (isc_sockaddr_getport(&addrs[i]) == 0) { + isc_sockaddr_setport(&addrs[i], port); + } + } + INSIST(i == count); + + *addrsp = addrs; + *countp = count; + + return (ISC_R_SUCCESS); +} + +void +named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, + uint32_t count) { + INSIST(addrsp != NULL && *addrsp != NULL); + + isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t)); + *addrsp = NULL; +} + +static isc_result_t +getremotesdef(const cfg_obj_t *cctx, const char *list, const char *name, + const cfg_obj_t **ret) { + isc_result_t result; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *elt; + + REQUIRE(cctx != NULL); + REQUIRE(name != NULL); + REQUIRE(ret != NULL && *ret == NULL); + + result = cfg_map_get(cctx, list, &obj); + if (result != ISC_R_SUCCESS) { + return (result); + } + elt = cfg_list_first(obj); + while (elt != NULL) { + obj = cfg_listelt_value(elt); + if (strcasecmp(cfg_obj_asstring(cfg_tuple_get(obj, "name")), + name) == 0) + { + *ret = obj; + return (ISC_R_SUCCESS); + } + elt = cfg_list_next(elt); + } + return (ISC_R_NOTFOUND); +} + +isc_result_t +named_config_getremotesdef(const cfg_obj_t *cctx, const char *list, + const char *name, const cfg_obj_t **ret) { + isc_result_t result; + + if (strcmp(list, "parental-agents") == 0) { + return (getremotesdef(cctx, list, name, ret)); + } else if (strcmp(list, "primaries") == 0) { + result = getremotesdef(cctx, list, name, ret); + if (result != ISC_R_SUCCESS) { + result = getremotesdef(cctx, "masters", name, ret); + } + return (result); + } + return (ISC_R_NOTFOUND); +} + +static isc_result_t +named_config_getname(isc_mem_t *mctx, const cfg_obj_t *obj, + dns_name_t **namep) { + REQUIRE(namep != NULL && *namep == NULL); + + const char *objstr; + isc_result_t result; + isc_buffer_t b; + dns_fixedname_t fname; + + if (!cfg_obj_isstring(obj)) { + *namep = NULL; + return (ISC_R_SUCCESS); + } + + *namep = isc_mem_get(mctx, sizeof(**namep)); + dns_name_init(*namep, NULL); + + objstr = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, objstr, strlen(objstr)); + isc_buffer_add(&b, strlen(objstr)); + dns_fixedname_init(&fname); + result = dns_name_fromtext(dns_fixedname_name(&fname), &b, dns_rootname, + 0, NULL); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, *namep, sizeof(**namep)); + *namep = NULL; + return (result); + } + dns_name_dup(dns_fixedname_name(&fname), mctx, *namep); + + return (ISC_R_SUCCESS); +} + +#define grow_array(mctx, array, newlen, oldlen) \ + if (newlen >= oldlen) { \ + size_t newsize = (newlen + 16) * sizeof(array[0]); \ + size_t oldsize = oldlen * sizeof(array[0]); \ + void *tmp = isc_mem_get(mctx, newsize); \ + memset(tmp, 0, newsize); \ + if (oldlen != 0) { \ + memmove(tmp, array, oldsize); \ + isc_mem_put(mctx, array, oldsize); \ + } \ + array = tmp; \ + oldlen = newlen + 16; \ + } + +#define shrink_array(mctx, array, newlen, oldlen) \ + if (newlen < oldlen) { \ + void *tmp = NULL; \ + size_t newsize = newlen * sizeof(array[0]); \ + size_t oldsize = oldlen * sizeof(array[0]); \ + if (newlen != 0) { \ + tmp = isc_mem_get(mctx, newsize); \ + memset(tmp, 0, newsize); \ + memmove(tmp, array, newsize); \ + } else { \ + tmp = NULL; \ + } \ + isc_mem_put(mctx, array, oldsize); \ + array = tmp; \ + oldlen = newlen; \ + } + +isc_result_t +named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype, + const cfg_obj_t *list, isc_mem_t *mctx, + dns_ipkeylist_t *ipkl) { + uint32_t addrcount = 0, keycount = 0, tlscount = 0, i = 0; + uint32_t listcount = 0, l = 0, j; + uint32_t stackcount = 0, pushed = 0; + isc_result_t result; + const cfg_listelt_t *element; + const cfg_obj_t *addrlist; + const cfg_obj_t *portobj; + in_port_t port = (in_port_t)0; + in_port_t def_port; + in_port_t def_tlsport; + isc_sockaddr_t *addrs = NULL; + dns_name_t **keys = NULL; + dns_name_t **tlss = NULL; + struct { + const char *name; + in_port_t port; + isc_sockaddr_t *src4s; + isc_sockaddr_t *src6s; + } *lists = NULL; + struct { + const cfg_listelt_t *element; + in_port_t port; + } *stack = NULL; + + REQUIRE(ipkl != NULL); + REQUIRE(ipkl->count == 0); + REQUIRE(ipkl->addrs == NULL); + REQUIRE(ipkl->keys == NULL); + REQUIRE(ipkl->tlss == NULL); + REQUIRE(ipkl->labels == NULL); + REQUIRE(ipkl->allocated == 0); + + /* + * Get system defaults. + */ + result = named_config_getport(config, "port", &def_port); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = named_config_getport(config, "tls-port", &def_tlsport); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + +newlist: + addrlist = cfg_tuple_get(list, "addresses"); + portobj = cfg_tuple_get(list, "port"); + + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + result = ISC_R_RANGE; + goto cleanup; + } + port = (in_port_t)val; + } + + result = ISC_R_NOMEMORY; + + element = cfg_list_first(addrlist); +resume: + for (; element != NULL; element = cfg_list_next(element)) { + const cfg_obj_t *addr; + const cfg_obj_t *key; + const cfg_obj_t *tls; + + addr = cfg_tuple_get(cfg_listelt_value(element), + "remoteselement"); + key = cfg_tuple_get(cfg_listelt_value(element), "key"); + tls = cfg_tuple_get(cfg_listelt_value(element), "tls"); + + if (!cfg_obj_issockaddr(addr)) { + const char *listname = cfg_obj_asstring(addr); + isc_result_t tresult; + + /* Grow lists? */ + grow_array(mctx, lists, l, listcount); + + /* Seen? */ + for (j = 0; j < l; j++) { + if (strcasecmp(lists[j].name, listname) == 0) { + break; + } + } + if (j < l) { + continue; + } + list = NULL; + tresult = named_config_getremotesdef(config, listtype, + listname, &list); + if (tresult == ISC_R_NOTFOUND) { + cfg_obj_log(addr, named_g_lctx, ISC_LOG_ERROR, + "%s \"%s\" not found", listtype, + listname); + + result = tresult; + goto cleanup; + } + if (tresult != ISC_R_SUCCESS) { + goto cleanup; + } + lists[l++].name = listname; + /* Grow stack? */ + grow_array(mctx, stack, pushed, stackcount); + /* + * We want to resume processing this list on the + * next element. + */ + stack[pushed].element = cfg_list_next(element); + stack[pushed].port = port; + pushed++; + goto newlist; + } + + grow_array(mctx, addrs, i, addrcount); + grow_array(mctx, keys, i, keycount); + grow_array(mctx, tlss, i, tlscount); + + addrs[i] = *cfg_obj_assockaddr(addr); + + result = named_config_getname(mctx, key, &keys[i]); + if (result != ISC_R_SUCCESS) { + i++; /* Increment here so that cleanup on error works. + */ + goto cleanup; + } + + result = named_config_getname(mctx, tls, &tlss[i]); + if (result != ISC_R_SUCCESS) { + i++; /* Increment here so that cleanup on error works. + */ + goto cleanup; + } + + /* If the port is unset, take it from one of the upper levels */ + if (isc_sockaddr_getport(&addrs[i]) == 0) { + in_port_t addr_port = port; + + /* If unset, use the default port or tls-port */ + if (addr_port == 0) { + if (tlss[i] != NULL) { + addr_port = def_tlsport; + } else { + addr_port = def_port; + } + } + + isc_sockaddr_setport(&addrs[i], addr_port); + } + + i++; + } + if (pushed != 0) { + pushed--; + element = stack[pushed].element; + port = stack[pushed].port; + goto resume; + } + + shrink_array(mctx, addrs, i, addrcount); + shrink_array(mctx, keys, i, keycount); + shrink_array(mctx, tlss, i, tlscount); + + if (lists != NULL) { + isc_mem_put(mctx, lists, listcount * sizeof(lists[0])); + } + if (stack != NULL) { + isc_mem_put(mctx, stack, stackcount * sizeof(stack[0])); + } + + INSIST(keycount == addrcount); + INSIST(tlscount == addrcount); + + ipkl->addrs = addrs; + ipkl->keys = keys; + ipkl->tlss = tlss; + ipkl->count = addrcount; + ipkl->allocated = addrcount; + + return (ISC_R_SUCCESS); + +cleanup: + if (addrs != NULL) { + isc_mem_put(mctx, addrs, addrcount * sizeof(addrs[0])); + } + if (keys != NULL) { + for (j = 0; j < i; j++) { + if (keys[j] == NULL) { + continue; + } + if (dns_name_dynamic(keys[j])) { + dns_name_free(keys[j], mctx); + } + isc_mem_put(mctx, keys[j], sizeof(*keys[j])); + } + isc_mem_put(mctx, keys, keycount * sizeof(keys[0])); + } + if (tlss != NULL) { + for (j = 0; j < i; j++) { + if (tlss[j] == NULL) { + continue; + } + if (dns_name_dynamic(tlss[j])) { + dns_name_free(tlss[j], mctx); + } + isc_mem_put(mctx, tlss[j], sizeof(*tlss[j])); + } + isc_mem_put(mctx, tlss, tlscount * sizeof(tlss[0])); + } + if (lists != NULL) { + isc_mem_put(mctx, lists, listcount * sizeof(lists[0])); + } + if (stack != NULL) { + isc_mem_put(mctx, stack, stackcount * sizeof(stack[0])); + } + return (result); +} + +isc_result_t +named_config_getport(const cfg_obj_t *config, const char *type, + in_port_t *portp) { + const cfg_obj_t *maps[3]; + const cfg_obj_t *options = NULL; + const cfg_obj_t *portobj = NULL; + isc_result_t result; + int i; + + (void)cfg_map_get(config, "options", &options); + i = 0; + if (options != NULL) { + maps[i++] = options; + } + maps[i++] = named_g_defaults; + maps[i] = NULL; + + result = named_config_get(maps, type, &portobj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_asuint32(portobj) >= UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR, + "port '%u' out of range", + cfg_obj_asuint32(portobj)); + return (ISC_R_RANGE); + } + *portp = (in_port_t)cfg_obj_asuint32(portobj); + return (ISC_R_SUCCESS); +} + +struct keyalgorithms { + const char *str; + enum { + hmacnone, + hmacmd5, + hmacsha1, + hmacsha224, + hmacsha256, + hmacsha384, + hmacsha512 + } hmac; + unsigned int type; + uint16_t size; +} algorithms[] = { { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 }, + { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 }, + { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 }, + { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 }, + { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 }, + { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 }, + { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 }, + { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 }, + { NULL, hmacnone, DST_ALG_UNKNOWN, 0 } }; + +isc_result_t +named_config_getkeyalgorithm(const char *str, const dns_name_t **name, + uint16_t *digestbits) { + return (named_config_getkeyalgorithm2(str, name, NULL, digestbits)); +} + +isc_result_t +named_config_getkeyalgorithm2(const char *str, const dns_name_t **name, + unsigned int *typep, uint16_t *digestbits) { + int i; + size_t len = 0; + uint16_t bits; + isc_result_t result; + + for (i = 0; algorithms[i].str != NULL; i++) { + len = strlen(algorithms[i].str); + if (strncasecmp(algorithms[i].str, str, len) == 0 && + (str[len] == '\0' || + (algorithms[i].size != 0 && str[len] == '-'))) + { + break; + } + } + if (algorithms[i].str == NULL) { + return (ISC_R_NOTFOUND); + } + if (str[len] == '-') { + result = isc_parse_uint16(&bits, str + len + 1, 10); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (bits > algorithms[i].size) { + return (ISC_R_RANGE); + } + } else if (algorithms[i].size == 0) { + bits = 128; + } else { + bits = algorithms[i].size; + } + + if (name != NULL) { + switch (algorithms[i].hmac) { + case hmacmd5: + *name = dns_tsig_hmacmd5_name; + break; + case hmacsha1: + *name = dns_tsig_hmacsha1_name; + break; + case hmacsha224: + *name = dns_tsig_hmacsha224_name; + break; + case hmacsha256: + *name = dns_tsig_hmacsha256_name; + break; + case hmacsha384: + *name = dns_tsig_hmacsha384_name; + break; + case hmacsha512: + *name = dns_tsig_hmacsha512_name; + break; + default: + UNREACHABLE(); + } + } + if (typep != NULL) { + *typep = algorithms[i].type; + } + if (digestbits != NULL) { + *digestbits = bits; + } + return (ISC_R_SUCCESS); +} diff --git a/bin/named/control.c b/bin/named/control.c new file mode 100644 index 0000000..454c128 --- /dev/null +++ b/bin/named/control.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_LIBSCF +#include +#endif /* ifdef HAVE_LIBSCF */ + +static isc_result_t +getcommand(isc_lex_t *lex, char **cmdp) { + isc_result_t result; + isc_token_t token; + + REQUIRE(cmdp != NULL && *cmdp == NULL); + + result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF, &token); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_lex_ungettoken(lex, &token); + + if (token.type != isc_tokentype_string) { + return (ISC_R_FAILURE); + } + + *cmdp = token.value.as_textregion.base; + + return (ISC_R_SUCCESS); +} + +static bool +command_compare(const char *str, const char *command) { + return (strcasecmp(str, command) == 0); +} + +/*% + * This function is called to process the incoming command + * when a control channel message is received. + */ +isc_result_t +named_control_docommand(isccc_sexpr_t *message, bool readonly, + isc_buffer_t **text) { + isccc_sexpr_t *data; + char *cmdline = NULL; + char *command = NULL; + isc_result_t result; + int log_level; + isc_buffer_t src; + isc_lex_t *lex = NULL; +#ifdef HAVE_LIBSCF + named_smf_want_disable = 0; +#endif /* ifdef HAVE_LIBSCF */ + + data = isccc_alist_lookup(message, "_data"); + if (!isccc_alist_alistp(data)) { + /* + * No data section. + */ + return (ISC_R_FAILURE); + } + + result = isccc_cc_lookupstring(data, "type", &cmdline); + if (result != ISC_R_SUCCESS) { + /* + * We have no idea what this is. + */ + return (result); + } + + result = isc_lex_create(named_g_mctx, strlen(cmdline), &lex); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_buffer_init(&src, cmdline, strlen(cmdline)); + isc_buffer_add(&src, strlen(cmdline)); + result = isc_lex_openbuffer(lex, &src); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = getcommand(lex, &command); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * Compare the 'command' parameter against all known control commands. + */ + if ((command_compare(command, NAMED_COMMAND_NULL) && + strlen(cmdline) == 4) || + command_compare(command, NAMED_COMMAND_STATUS)) + { + log_level = ISC_LOG_DEBUG(1); + } else { + log_level = ISC_LOG_INFO; + } + + /* + * If this listener should have read-only access, reject + * restricted commands here. rndc nta is handled specially + * below. + */ + if (readonly && !command_compare(command, NAMED_COMMAND_NTA) && + !command_compare(command, NAMED_COMMAND_NULL) && + !command_compare(command, NAMED_COMMAND_STATUS) && + !command_compare(command, NAMED_COMMAND_SHOWZONE) && + !command_compare(command, NAMED_COMMAND_TESTGEN) && + !command_compare(command, NAMED_COMMAND_ZONESTATUS)) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, log_level, + "rejecting restricted control channel " + "command '%s'", + cmdline); + result = ISC_R_FAILURE; + goto cleanup; + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, log_level, + "received control channel command '%s'", cmdline); + + /* + * After the lengthy "halt" and "stop", the commands are + * handled in alphabetical order of the NAMED_COMMAND_ macros. + */ + if (command_compare(command, NAMED_COMMAND_HALT)) { +#ifdef HAVE_LIBSCF + /* + * If we are managed by smf(5), AND in chroot, then + * we cannot connect to the smf repository, so just + * return with an appropriate message back to rndc. + */ + if (named_smf_got_instance == 1 && named_smf_chroot == 1) { + result = named_smf_add_message(text); + goto cleanup; + } + /* + * If we are managed by smf(5) but not in chroot, + * try to disable ourselves the smf way. + */ + if (named_smf_got_instance == 1 && named_smf_chroot == 0) { + named_smf_want_disable = 1; + } + /* + * If named_smf_got_instance = 0, named_smf_chroot + * is not relevant and we fall through to + * isc_app_shutdown below. + */ +#endif /* ifdef HAVE_LIBSCF */ + /* Do not flush master files */ + named_server_flushonshutdown(named_g_server, false); + named_os_shutdownmsg(cmdline, *text); + result = ISC_R_SHUTTINGDOWN; + } else if (command_compare(command, NAMED_COMMAND_STOP)) { + /* + * "stop" is the same as "halt" except it does + * flush master files. + */ +#ifdef HAVE_LIBSCF + if (named_smf_got_instance == 1 && named_smf_chroot == 1) { + result = named_smf_add_message(text); + goto cleanup; + } + if (named_smf_got_instance == 1 && named_smf_chroot == 0) { + named_smf_want_disable = 1; + } +#endif /* ifdef HAVE_LIBSCF */ + named_server_flushonshutdown(named_g_server, true); + named_os_shutdownmsg(cmdline, *text); + result = ISC_R_SHUTTINGDOWN; + } else if (command_compare(command, NAMED_COMMAND_ADDZONE) || + command_compare(command, NAMED_COMMAND_MODZONE)) + { + result = named_server_changezone(named_g_server, cmdline, text); + } else if (command_compare(command, NAMED_COMMAND_DELZONE)) { + result = named_server_delzone(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_DNSSEC)) { + result = named_server_dnssec(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_DNSTAP) || + command_compare(command, NAMED_COMMAND_DNSTAPREOPEN)) + { + result = named_server_dnstap(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_DUMPDB)) { + named_server_dumpdb(named_g_server, lex, text); + result = ISC_R_SUCCESS; + } else if (command_compare(command, NAMED_COMMAND_DUMPSTATS)) { + result = named_server_dumpstats(named_g_server); + } else if (command_compare(command, NAMED_COMMAND_FLUSH)) { + result = named_server_flushcache(named_g_server, lex); + } else if (command_compare(command, NAMED_COMMAND_FLUSHNAME)) { + result = named_server_flushnode(named_g_server, lex, false); + } else if (command_compare(command, NAMED_COMMAND_FLUSHTREE)) { + result = named_server_flushnode(named_g_server, lex, true); + } else if (command_compare(command, NAMED_COMMAND_FREEZE)) { + result = named_server_freeze(named_g_server, true, lex, text); + } else if (command_compare(command, NAMED_COMMAND_LOADKEYS) || + command_compare(command, NAMED_COMMAND_SIGN)) + { + result = named_server_rekey(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_MKEYS)) { + result = named_server_mkeys(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_NOTIFY)) { + result = named_server_notifycommand(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_NOTRACE)) { + named_g_debuglevel = 0; + isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); + result = ISC_R_SUCCESS; + } else if (command_compare(command, NAMED_COMMAND_NTA)) { + result = named_server_nta(named_g_server, lex, readonly, text); + } else if (command_compare(command, NAMED_COMMAND_NULL)) { + result = ISC_R_SUCCESS; + } else if (command_compare(command, NAMED_COMMAND_QUERYLOG)) { + result = named_server_togglequerylog(named_g_server, lex); + } else if (command_compare(command, NAMED_COMMAND_RECONFIG)) { + result = named_server_reconfigcommand(named_g_server); + } else if (command_compare(command, NAMED_COMMAND_RECURSING)) { + result = named_server_dumprecursing(named_g_server); + } else if (command_compare(command, NAMED_COMMAND_REFRESH)) { + result = named_server_refreshcommand(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_RELOAD)) { + result = named_server_reloadcommand(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_RETRANSFER)) { + result = named_server_retransfercommand(named_g_server, lex, + text); + } else if (command_compare(command, NAMED_COMMAND_SCAN)) { + named_server_scan_interfaces(named_g_server); + result = ISC_R_SUCCESS; + } else if (command_compare(command, NAMED_COMMAND_SECROOTS)) { + result = named_server_dumpsecroots(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_SERVESTALE)) { + result = named_server_servestale(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_SHOWZONE)) { + result = named_server_showzone(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_SIGNING)) { + result = named_server_signing(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_STATUS)) { + result = named_server_status(named_g_server, text); + } else if (command_compare(command, NAMED_COMMAND_SYNC)) { + result = named_server_sync(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_TCPTIMEOUTS)) { + result = named_server_tcptimeouts(lex, text); + } else if (command_compare(command, NAMED_COMMAND_TESTGEN)) { + result = named_server_testgen(lex, text); + } else if (command_compare(command, NAMED_COMMAND_THAW) || + command_compare(command, NAMED_COMMAND_UNFREEZE)) + { + result = named_server_freeze(named_g_server, false, lex, text); + } else if (command_compare(command, NAMED_COMMAND_TIMERPOKE)) { + isc_timermgr_poke(named_g_timermgr); + result = ISC_R_SUCCESS; + } else if (command_compare(command, NAMED_COMMAND_TRACE)) { + result = named_server_setdebuglevel(named_g_server, lex); + } else if (command_compare(command, NAMED_COMMAND_TSIGDELETE)) { + result = named_server_tsigdelete(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_TSIGLIST)) { + result = named_server_tsiglist(named_g_server, text); + } else if (command_compare(command, NAMED_COMMAND_VALIDATION)) { + result = named_server_validation(named_g_server, lex, text); + } else if (command_compare(command, NAMED_COMMAND_ZONESTATUS)) { + result = named_server_zonestatus(named_g_server, lex, text); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, + "unknown control channel command '%s'", command); + result = DNS_R_UNKNOWNCOMMAND; + } + +cleanup: + if (lex != NULL) { + isc_lex_destroy(&lex); + } + + return (result); +} diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c new file mode 100644 index 0000000..d402155 --- /dev/null +++ b/bin/named/controlconf.c @@ -0,0 +1,1494 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +typedef struct controlkey controlkey_t; +typedef ISC_LIST(controlkey_t) controlkeylist_t; + +typedef struct controlconnection controlconnection_t; +typedef ISC_LIST(controlconnection_t) controlconnectionlist_t; + +typedef struct controllistener controllistener_t; +typedef ISC_LIST(controllistener_t) controllistenerlist_t; + +struct controlkey { + char *keyname; + uint32_t algorithm; + isc_region_t secret; + ISC_LINK(controlkey_t) link; +}; + +struct controlconnection { + isc_nmhandle_t *readhandle; + isc_nmhandle_t *sendhandle; + isc_nmhandle_t *cmdhandle; + isccc_ccmsg_t ccmsg; + bool reading; + bool sending; + controllistener_t *listener; + isccc_sexpr_t *ctrl; + isc_buffer_t *buffer; + isc_buffer_t *text; + isccc_sexpr_t *request; + isccc_sexpr_t *response; + uint32_t alg; + isccc_region_t secret; + uint32_t nonce; + isc_stdtime_t now; + isc_result_t result; + ISC_LINK(controlconnection_t) link; +}; + +struct controllistener { + named_controls_t *controls; + isc_mem_t *mctx; + isc_sockaddr_t address; + isc_nmsocket_t *sock; + dns_acl_t *acl; + bool exiting; + isc_refcount_t refs; + controlkeylist_t keys; + isc_mutex_t connections_lock; + controlconnectionlist_t connections; + isc_socktype_t type; + uint32_t perm; + uint32_t owner; + uint32_t group; + bool readonly; + ISC_LINK(controllistener_t) link; +}; + +struct named_controls { + named_server_t *server; + controllistenerlist_t listeners; + atomic_bool shuttingdown; + isc_mutex_t symtab_lock; + isccc_symtab_t *symtab; +}; + +static isc_result_t +control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg); +static void +control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg); + +#define CLOCKSKEW 300 + +static void +free_controlkey(controlkey_t *key, isc_mem_t *mctx) { + if (key->keyname != NULL) { + isc_mem_free(mctx, key->keyname); + } + if (key->secret.base != NULL) { + isc_mem_put(mctx, key->secret.base, key->secret.length); + } + isc_mem_put(mctx, key, sizeof(*key)); +} + +static void +free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { + while (!ISC_LIST_EMPTY(*keylist)) { + controlkey_t *key = ISC_LIST_HEAD(*keylist); + ISC_LIST_UNLINK(*keylist, key, link); + free_controlkey(key, mctx); + } +} + +static void +free_listener(controllistener_t *listener) { + INSIST(listener->exiting); + INSIST(ISC_LIST_EMPTY(listener->connections)); + + isc_refcount_destroy(&listener->refs); + + if (listener->sock != NULL) { + isc_nmsocket_close(&listener->sock); + } + + free_controlkeylist(&listener->keys, listener->mctx); + + if (listener->acl != NULL) { + dns_acl_detach(&listener->acl); + } + isc_mutex_destroy(&listener->connections_lock); + + isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); +} + +static void +maybe_free_listener(controllistener_t *listener) { + if (isc_refcount_decrement(&listener->refs) == 1) { + free_listener(listener); + } +} + +static void +shutdown_listener(controllistener_t *listener) { + if (!listener->exiting) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + + ISC_LIST_UNLINK(listener->controls->listeners, listener, link); + + isc_sockaddr_format(&listener->address, socktext, + sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, + "stopping command channel on %s", socktext); +#if 0 + /* XXX: no unix domain socket support */ + if (listener->type == isc_socktype_unix) { + isc_socket_cleanunix(&listener->address, true); + } +#endif + listener->exiting = true; + } + + isc_nm_stoplistening(listener->sock); + maybe_free_listener(listener); +} + +static bool +address_ok(isc_sockaddr_t *sockaddr, controllistener_t *listener) { + dns_aclenv_t *env = + ns_interfacemgr_getaclenv(named_g_server->interfacemgr); + isc_netaddr_t netaddr; + isc_result_t result; + int match; + + /* ACL doesn't apply to unix domain sockets */ + if (listener->type != isc_socktype_tcp) { + return (true); + } + + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + + result = dns_acl_match(&netaddr, NULL, listener->acl, env, &match, + NULL); + return (result == ISC_R_SUCCESS && match > 0); +} + +static void +control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + controlconnection_t *conn = (controlconnection_t *)arg; + controllistener_t *listener = conn->listener; + isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle); + + REQUIRE(conn->sending); + + conn->sending = false; + + if (conn->result == ISC_R_SHUTTINGDOWN) { + isc_app_shutdown(); + goto cleanup_sendhandle; + } + + if (atomic_load_acquire(&listener->controls->shuttingdown) || + result == ISC_R_SHUTTINGDOWN) + { + goto cleanup_sendhandle; + } else if (result != ISC_R_SUCCESS) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, + "error sending command response to %s: %s", + socktext, isc_result_totext(result)); + goto cleanup_sendhandle; + } + + isc_nmhandle_attach(handle, &conn->readhandle); + conn->reading = true; + + isc_nmhandle_detach(&conn->sendhandle); + + isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn); + return; + +cleanup_sendhandle: + isc_nmhandle_detach(&conn->sendhandle); +} + +static void +log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(ccmsg->handle); + + isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_ERROR, + "invalid command from %s: %s", socktext, + isc_result_totext(result)); +} + +static void +conn_cleanup(controlconnection_t *conn) { + controllistener_t *listener = conn->listener; + + if (conn->response != NULL) { + isccc_sexpr_free(&conn->response); + } + if (conn->request != NULL) { + isccc_sexpr_free(&conn->request); + } + if (conn->secret.rstart != NULL) { + isc_mem_put(listener->mctx, conn->secret.rstart, + REGION_SIZE(conn->secret)); + } + if (conn->text != NULL) { + isc_buffer_free(&conn->text); + } +} + +static void +control_respond(isc_nmhandle_t *handle, controlconnection_t *conn) { + controllistener_t *listener = conn->listener; + isccc_sexpr_t *data = NULL; + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + + result = isccc_cc_createresponse(conn->request, conn->now, + conn->now + 60, &conn->response); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (conn->result == ISC_R_SHUTTINGDOWN) { + result = ISC_R_SUCCESS; + } else { + result = conn->result; + } + + data = isccc_alist_lookup(conn->response, "_data"); + if (data != NULL) { + if (isccc_cc_defineuint32(data, "result", result) == NULL) { + goto cleanup; + } + } + + if (result != ISC_R_SUCCESS) { + if (data != NULL) { + const char *estr = isc_result_totext(result); + if (isccc_cc_definestring(data, "err", estr) == NULL) { + goto cleanup; + } + } + } + + if (isc_buffer_usedlength(conn->text) > 0) { + if (data != NULL) { + char *str = (char *)isc_buffer_base(conn->text); + if (isccc_cc_definestring(data, "text", str) == NULL) { + goto cleanup; + } + } + } + + conn->ctrl = isccc_alist_lookup(conn->response, "_ctrl"); + if (conn->ctrl == NULL || + isccc_cc_defineuint32(conn->ctrl, "_nonce", conn->nonce) == NULL) + { + goto cleanup; + } + + if (conn->buffer == NULL) { + isc_buffer_allocate(listener->mctx, &conn->buffer, 2 * 2048); + } + + isc_buffer_clear(conn->buffer); + /* Skip the length field (4 bytes) */ + isc_buffer_add(conn->buffer, 4); + + result = isccc_cc_towire(conn->response, &conn->buffer, conn->alg, + &conn->secret); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + isc_buffer_init(&b, conn->buffer->base, 4); + isc_buffer_putuint32(&b, conn->buffer->used - 4); + + r.base = conn->buffer->base; + r.length = conn->buffer->used; + + isc_nmhandle_attach(handle, &conn->sendhandle); + conn->sending = true; + conn_cleanup(conn); + + isc_nmhandle_detach(&conn->cmdhandle); + + isc_nm_send(conn->sendhandle, &r, control_senddone, conn); + + return; + +cleanup: + conn_cleanup(conn); + isc_nmhandle_detach(&conn->cmdhandle); +} + +static void +control_command(isc_task_t *task, isc_event_t *event) { + controlconnection_t *conn = event->ev_arg; + controllistener_t *listener = conn->listener; + + UNUSED(task); + + if (atomic_load_acquire(&listener->controls->shuttingdown)) { + conn_cleanup(conn); + isc_nmhandle_detach(&conn->cmdhandle); + goto done; + } + + conn->result = named_control_docommand(conn->request, + listener->readonly, &conn->text); + control_respond(conn->cmdhandle, conn); + +done: + isc_event_free(&event); +} + +static void +control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + controlconnection_t *conn = (controlconnection_t *)arg; + controllistener_t *listener = conn->listener; + controlkey_t *key = NULL; + isc_event_t *event = NULL; + isccc_time_t sent; + isccc_time_t exp; + uint32_t nonce; + + conn->reading = false; + + /* Is the server shutting down? */ + if (atomic_load_acquire(&listener->controls->shuttingdown)) { + goto cleanup_readhandle; + } + + if (result != ISC_R_SUCCESS) { + if (result == ISC_R_SHUTTINGDOWN) { + atomic_store_release(&listener->controls->shuttingdown, + true); + } else if (result != ISC_R_EOF) { + log_invalid(&conn->ccmsg, result); + } + + goto cleanup_readhandle; + } + + for (key = ISC_LIST_HEAD(listener->keys); key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + isccc_region_t ccregion; + + ccregion.rstart = isc_buffer_base(conn->ccmsg.buffer); + ccregion.rend = isc_buffer_used(conn->ccmsg.buffer); + conn->secret.rstart = isc_mem_get(listener->mctx, + key->secret.length); + memmove(conn->secret.rstart, key->secret.base, + key->secret.length); + conn->secret.rend = conn->secret.rstart + key->secret.length; + conn->alg = key->algorithm; + result = isccc_cc_fromwire(&ccregion, &conn->request, conn->alg, + &conn->secret); + if (result == ISC_R_SUCCESS) { + break; + } + isc_mem_put(listener->mctx, conn->secret.rstart, + REGION_SIZE(conn->secret)); + } + + if (key == NULL) { + log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); + goto cleanup; + } + + /* We shouldn't be getting a reply. */ + if (isccc_cc_isreply(conn->request)) { + log_invalid(&conn->ccmsg, ISC_R_FAILURE); + goto cleanup; + } + + isc_stdtime_get(&conn->now); + + /* + * Limit exposure to replay attacks. + */ + conn->ctrl = isccc_alist_lookup(conn->request, "_ctrl"); + if (!isccc_alist_alistp(conn->ctrl)) { + log_invalid(&conn->ccmsg, ISC_R_FAILURE); + goto cleanup; + } + + if (isccc_cc_lookupuint32(conn->ctrl, "_tim", &sent) == ISC_R_SUCCESS) { + if ((sent + CLOCKSKEW) < conn->now || + (sent - CLOCKSKEW) > conn->now) + { + log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); + goto cleanup; + } + } else { + log_invalid(&conn->ccmsg, ISC_R_FAILURE); + goto cleanup; + } + + /* + * Expire messages that are too old. + */ + if (isccc_cc_lookupuint32(conn->ctrl, "_exp", &exp) == ISC_R_SUCCESS && + conn->now > exp) + { + log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); + goto cleanup; + } + + /* + * Duplicate suppression (required for UDP). + */ + LOCK(&listener->controls->symtab_lock); + isccc_cc_cleansymtab(listener->controls->symtab, conn->now); + result = isccc_cc_checkdup(listener->controls->symtab, conn->request, + conn->now); + UNLOCK(&listener->controls->symtab_lock); + if (result != ISC_R_SUCCESS) { + if (result == ISC_R_EXISTS) { + result = ISCCC_R_DUPLICATE; + } + log_invalid(&conn->ccmsg, result); + goto cleanup; + } + + if (conn->nonce != 0 && + (isccc_cc_lookupuint32(conn->ctrl, "_nonce", &nonce) != + ISC_R_SUCCESS || + conn->nonce != nonce)) + { + log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); + goto cleanup; + } + + isc_buffer_allocate(listener->mctx, &conn->text, 2 * 2048); + + isc_nmhandle_attach(handle, &conn->cmdhandle); + isc_nmhandle_detach(&conn->readhandle); + + if (conn->nonce == 0) { + /* + * Establish nonce. + */ + while (conn->nonce == 0) { + isc_nonce_buf(&conn->nonce, sizeof(conn->nonce)); + } + conn->result = ISC_R_SUCCESS; + control_respond(handle, conn); + return; + } + + /* + * Trigger the command. + */ + + event = isc_event_allocate(listener->mctx, conn, NAMED_EVENT_COMMAND, + control_command, conn, sizeof(isc_event_t)); + isc_task_send(named_g_server->task, &event); + + return; + +cleanup: + conn_cleanup(conn); + +cleanup_readhandle: + /* + * readhandle could be NULL if we're shutting down, + * but if not we need to detach it. + */ + if (conn->readhandle != NULL) { + isc_nmhandle_detach(&conn->readhandle); + } +} + +static void +conn_reset(void *arg) { + controlconnection_t *conn = (controlconnection_t *)arg; + controllistener_t *listener = conn->listener; + + if (conn->buffer != NULL) { + isc_buffer_free(&conn->buffer); + } + + if (conn->reading) { + isccc_ccmsg_cancelread(&conn->ccmsg); + return; + } + + LOCK(&listener->connections_lock); + ISC_LIST_UNLINK(listener->connections, conn, link); + UNLOCK(&listener->connections_lock); +#ifdef ENABLE_AFL + if (named_g_fuzz_type == isc_fuzz_rndc) { + named_fuzz_notify(); + } +#endif /* ifdef ENABLE_AFL */ + + isccc_ccmsg_invalidate(&conn->ccmsg); +} + +static void +conn_put(void *arg) { + controlconnection_t *conn = (controlconnection_t *)arg; + controllistener_t *listener = conn->listener; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), + "freeing control connection"); + maybe_free_listener(listener); +} + +static void +newconnection(controllistener_t *listener, isc_nmhandle_t *handle) { + controlconnection_t *conn = NULL; + + conn = isc_nmhandle_getdata(handle); + if (conn == NULL) { + conn = isc_nmhandle_getextra(handle); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), + "allocate new control connection"); + isc_nmhandle_setdata(handle, conn, conn_reset, conn_put); + isc_refcount_increment(&listener->refs); + } + + *conn = (controlconnection_t){ .listener = listener, + .reading = false, + .alg = DST_ALG_UNKNOWN }; + + isccc_ccmsg_init(listener->mctx, handle, &conn->ccmsg); + + /* Set a 32 KiB upper limit on incoming message. */ + isccc_ccmsg_setmaxsize(&conn->ccmsg, 32768); + + LOCK(&listener->connections_lock); + ISC_LIST_INITANDAPPEND(listener->connections, conn, link); + UNLOCK(&listener->connections_lock); + + isc_nmhandle_attach(handle, &conn->readhandle); + conn->reading = true; + + isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn); +} + +static isc_result_t +control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + controllistener_t *listener = arg; + isc_sockaddr_t peeraddr; + + if (result != ISC_R_SUCCESS) { + if (result == ISC_R_SHUTTINGDOWN) { + shutdown_listener(listener); + } + return (result); + } + + peeraddr = isc_nmhandle_peeraddr(handle); + if (!address_ok(&peeraddr, listener)) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, + "rejected command channel message from %s", + socktext); + return (ISC_R_FAILURE); + } + + newconnection(listener, handle); + return (ISC_R_SUCCESS); +} + +static void +controls_shutdown(named_controls_t *controls) { + controllistener_t *listener = NULL; + controllistener_t *next = NULL; + + for (listener = ISC_LIST_HEAD(controls->listeners); listener != NULL; + listener = next) + { + /* + * This is asynchronous. As listeners shut down, they will + * call their callbacks. + */ + next = ISC_LIST_NEXT(listener, link); + shutdown_listener(listener); + } +} + +void +named_controls_shutdown(named_controls_t *controls) { + controls_shutdown(controls); + atomic_store_release(&controls->shuttingdown, true); +} + +static isc_result_t +cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, + const cfg_obj_t **objp) { + const cfg_listelt_t *element = NULL; + const char *str = NULL; + const cfg_obj_t *obj = NULL; + + for (element = cfg_list_first(keylist); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(cfg_map_getname(obj)); + if (strcasecmp(str, keyname) == 0) { + break; + } + } + if (element == NULL) { + return (ISC_R_NOTFOUND); + } + obj = cfg_listelt_value(element); + *objp = obj; + return (ISC_R_SUCCESS); +} + +static void +controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, + controlkeylist_t *keyids) { + const cfg_listelt_t *element = NULL; + char *newstr = NULL; + const char *str = NULL; + const cfg_obj_t *obj = NULL; + controlkey_t *key = NULL; + + for (element = cfg_list_first(keylist); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + newstr = isc_mem_strdup(mctx, str); + key = isc_mem_get(mctx, sizeof(*key)); + key->keyname = newstr; + key->algorithm = DST_ALG_UNKNOWN; + key->secret.base = NULL; + key->secret.length = 0; + ISC_LINK_INIT(key, link); + ISC_LIST_APPEND(*keyids, key, link); + newstr = NULL; + } +} + +static void +register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, + controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) { + controlkey_t *keyid = NULL, *next = NULL; + const cfg_obj_t *keydef = NULL; + char secret[1024]; + isc_buffer_t b; + isc_result_t result; + + /* + * Find the keys corresponding to the keyids used by this listener. + */ + for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { + next = ISC_LIST_NEXT(keyid, link); + + result = cfgkeylist_find(keylist, keyid->keyname, &keydef); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't find key '%s' for use with " + "command channel %s", + keyid->keyname, socktext); + ISC_LIST_UNLINK(*keyids, keyid, link); + free_controlkey(keyid, mctx); + } else { + const cfg_obj_t *algobj = NULL; + const cfg_obj_t *secretobj = NULL; + const char *algstr = NULL; + const char *secretstr = NULL; + unsigned int algtype; + + (void)cfg_map_get(keydef, "algorithm", &algobj); + (void)cfg_map_get(keydef, "secret", &secretobj); + INSIST(algobj != NULL && secretobj != NULL); + + algstr = cfg_obj_asstring(algobj); + secretstr = cfg_obj_asstring(secretobj); + + result = named_config_getkeyalgorithm2(algstr, NULL, + &algtype, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(control, named_g_lctx, + ISC_LOG_WARNING, + "unsupported algorithm '%s' in " + "key '%s' for use with command " + "channel %s", + algstr, keyid->keyname, socktext); + ISC_LIST_UNLINK(*keyids, keyid, link); + free_controlkey(keyid, mctx); + continue; + } + + keyid->algorithm = algtype; + isc_buffer_init(&b, secret, sizeof(secret)); + result = isc_base64_decodestring(secretstr, &b); + + if (result != ISC_R_SUCCESS) { + cfg_obj_log(keydef, named_g_lctx, + ISC_LOG_WARNING, + "secret for key '%s' on " + "command channel %s: %s", + keyid->keyname, socktext, + isc_result_totext(result)); + ISC_LIST_UNLINK(*keyids, keyid, link); + free_controlkey(keyid, mctx); + continue; + } + + keyid->secret.length = isc_buffer_usedlength(&b); + keyid->secret.base = isc_mem_get(mctx, + keyid->secret.length); + memmove(keyid->secret.base, isc_buffer_base(&b), + keyid->secret.length); + } + } +} + +#define CHECK(x) \ + do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +static isc_result_t +get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { + isc_result_t result; + cfg_parser_t *pctx = NULL; + cfg_obj_t *config = NULL; + const cfg_obj_t *key = NULL; + const cfg_obj_t *algobj = NULL; + const cfg_obj_t *secretobj = NULL; + const char *algstr = NULL; + const char *secretstr = NULL; + controlkey_t *keyid = NULL; + char secret[1024]; + unsigned int algtype; + isc_buffer_t b; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_INFO, + "configuring command channel from '%s'", named_g_keyfile); + if (!isc_file_exists(named_g_keyfile)) { + return (ISC_R_FILENOTFOUND); + } + + CHECK(cfg_parser_create(mctx, named_g_lctx, &pctx)); + CHECK(cfg_parse_file(pctx, named_g_keyfile, &cfg_type_rndckey, + &config)); + CHECK(cfg_map_get(config, "key", &key)); + + keyid = isc_mem_get(mctx, sizeof(*keyid)); + keyid->keyname = isc_mem_strdup(mctx, + cfg_obj_asstring(cfg_map_getname(key))); + keyid->secret.base = NULL; + keyid->secret.length = 0; + keyid->algorithm = DST_ALG_UNKNOWN; + ISC_LINK_INIT(keyid, link); + if (keyid->keyname == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + CHECK(bind9_check_key(key, named_g_lctx)); + + (void)cfg_map_get(key, "algorithm", &algobj); + (void)cfg_map_get(key, "secret", &secretobj); + INSIST(algobj != NULL && secretobj != NULL); + + algstr = cfg_obj_asstring(algobj); + secretstr = cfg_obj_asstring(secretobj); + + result = named_config_getkeyalgorithm2(algstr, NULL, &algtype, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, + "unsupported algorithm '%s' in " + "key '%s' for use with command " + "channel", + algstr, keyid->keyname); + goto cleanup; + } + + keyid->algorithm = algtype; + isc_buffer_init(&b, secret, sizeof(secret)); + result = isc_base64_decodestring(secretstr, &b); + + if (result != ISC_R_SUCCESS) { + cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, + "secret for key '%s' on command channel: %s", + keyid->keyname, isc_result_totext(result)); + goto cleanup; + } + + keyid->secret.length = isc_buffer_usedlength(&b); + keyid->secret.base = isc_mem_get(mctx, keyid->secret.length); + memmove(keyid->secret.base, isc_buffer_base(&b), keyid->secret.length); + ISC_LIST_APPEND(*keyids, keyid, link); + keyid = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (keyid != NULL) { + free_controlkey(keyid, mctx); + } + if (config != NULL) { + cfg_obj_destroy(pctx, &config); + } + if (pctx != NULL) { + cfg_parser_destroy(&pctx); + } + return (result); +} + +/* + * Ensures that both '*global_keylistp' and '*control_keylistp' are + * valid or both are NULL. + */ +static void +get_key_info(const cfg_obj_t *config, const cfg_obj_t *control, + const cfg_obj_t **global_keylistp, + const cfg_obj_t **control_keylistp) { + isc_result_t result; + const cfg_obj_t *control_keylist = NULL; + const cfg_obj_t *global_keylist = NULL; + + REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); + REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); + + control_keylist = cfg_tuple_get(control, "keys"); + + if (!cfg_obj_isvoid(control_keylist) && + cfg_list_first(control_keylist) != NULL) + { + result = cfg_map_get(config, "key", &global_keylist); + + if (result == ISC_R_SUCCESS) { + *global_keylistp = global_keylist; + *control_keylistp = control_keylist; + } + } +} + +static void +update_listener(named_controls_t *cp, controllistener_t **listenerp, + const cfg_obj_t *control, const cfg_obj_t *config, + isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, + const char *socktext, isc_socktype_t type) { + controllistener_t *listener = NULL; + const cfg_obj_t *allow = NULL; + const cfg_obj_t *global_keylist = NULL; + const cfg_obj_t *control_keylist = NULL; + dns_acl_t *new_acl = NULL; + controlkeylist_t keys; + isc_result_t result = ISC_R_SUCCESS; + + for (listener = ISC_LIST_HEAD(cp->listeners); listener != NULL; + listener = ISC_LIST_NEXT(listener, link)) + { + if (isc_sockaddr_equal(addr, &listener->address)) { + break; + } + } + + if (listener == NULL) { + *listenerp = NULL; + return; + } + + /* + * There is already a listener for this sockaddr. + * Update the access list and key information. + * + * First try to deal with the key situation. There are a few + * possibilities: + * (a) It had an explicit keylist and still has an explicit keylist. + * (b) It had an automagic key and now has an explicit keylist. + * (c) It had an explicit keylist and now needs an automagic key. + * (d) It has an automagic key and still needs the automagic key. + * + * (c) and (d) are the annoying ones. The caller needs to know + * that it should use the automagic configuration for key information + * in place of the named.conf configuration. + * + * XXXDCL There is one other hazard that has not been dealt with, + * the problem that if a key change is being caused by a control + * channel reload, then the response will be with the new key + * and not able to be decrypted by the client. + */ + if (control != NULL) { + get_key_info(config, control, &global_keylist, + &control_keylist); + } + + if (control_keylist != NULL) { + INSIST(global_keylist != NULL); + + ISC_LIST_INIT(keys); + controlkeylist_fromcfg(control_keylist, listener->mctx, &keys); + free_controlkeylist(&listener->keys, listener->mctx); + listener->keys = keys; + register_keys(control, global_keylist, &listener->keys, + listener->mctx, socktext); + } else { + free_controlkeylist(&listener->keys, listener->mctx); + result = get_rndckey(listener->mctx, &listener->keys); + } + + if (result != ISC_R_SUCCESS && global_keylist != NULL) { + /* + * This message might be a little misleading since the + * "new keys" might in fact be identical to the old ones, + * but tracking whether they are identical just for the + * sake of avoiding this message would be too much trouble. + */ + if (control != NULL) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't install new keys for " + "command channel %s: %s", + socktext, isc_result_totext(result)); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, + "couldn't install new keys for " + "command channel %s: %s", + socktext, isc_result_totext(result)); + } + } + + /* + * Now, keep the old access list unless a new one can be made. + */ + if (control != NULL && type == isc_socktype_tcp) { + allow = cfg_tuple_get(control, "allow"); + result = cfg_acl_fromconfig(allow, config, named_g_lctx, + aclconfctx, listener->mctx, 0, + &new_acl); + } else { + result = dns_acl_any(listener->mctx, &new_acl); + } + + if (control != NULL) { + const cfg_obj_t *readonly = NULL; + + readonly = cfg_tuple_get(control, "read-only"); + if (!cfg_obj_isvoid(readonly)) { + listener->readonly = cfg_obj_asboolean(readonly); + } + } + + if (result == ISC_R_SUCCESS) { + dns_acl_detach(&listener->acl); + dns_acl_attach(new_acl, &listener->acl); + dns_acl_detach(&new_acl); + /* XXXDCL say the old acl is still used? */ + } else if (control != NULL) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't install new acl for " + "command channel %s: %s", + socktext, isc_result_totext(result)); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, + "couldn't install new acl for " + "command channel %s: %s", + socktext, isc_result_totext(result)); + } + +#if 0 + /* XXX: no unix socket support yet */ + if (result == ISC_R_SUCCESS && type == isc_socktype_unix) { + uint32_t perm, owner, group; + perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); + owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner")); + group = cfg_obj_asuint32(cfg_tuple_get(control, "group")); + result = ISC_R_SUCCESS; + if (listener->perm != perm || listener->owner != owner || + listener->group != group) + { + result = isc_socket_permunix(&listener->address, perm, + owner, group); + } + if (result == ISC_R_SUCCESS) { + listener->perm = perm; + listener->owner = owner; + listener->group = group; + } else if (control != NULL) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't update ownership/permission for " + "command channel %s", + socktext); + } + } +#endif + + *listenerp = listener; +} + +static void +add_listener(named_controls_t *cp, controllistener_t **listenerp, + const cfg_obj_t *control, const cfg_obj_t *config, + isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, + const char *socktext, isc_socktype_t type) { + isc_mem_t *mctx = cp->server->mctx; + controllistener_t *listener = NULL; + const cfg_obj_t *allow = NULL; + const cfg_obj_t *global_keylist = NULL; + const cfg_obj_t *control_keylist = NULL; + dns_acl_t *new_acl = NULL; + isc_result_t result = ISC_R_SUCCESS; + int pf; + + listener = isc_mem_get(mctx, sizeof(*listener)); + *listener = (controllistener_t){ .controls = cp, + .address = *addr, + .type = type }; + isc_mem_attach(mctx, &listener->mctx); + isc_mutex_init(&listener->connections_lock); + ISC_LINK_INIT(listener, link); + ISC_LIST_INIT(listener->keys); + ISC_LIST_INIT(listener->connections); + isc_refcount_init(&listener->refs, 1); + + /* + * Make the ACL. + */ + if (control != NULL && type == isc_socktype_tcp) { + const cfg_obj_t *readonly = NULL; + + allow = cfg_tuple_get(control, "allow"); + CHECK(cfg_acl_fromconfig(allow, config, named_g_lctx, + aclconfctx, mctx, 0, &new_acl)); + + readonly = cfg_tuple_get(control, "read-only"); + if (!cfg_obj_isvoid(readonly)) { + listener->readonly = cfg_obj_asboolean(readonly); + } + } else { + CHECK(dns_acl_any(mctx, &new_acl)); + } + + dns_acl_attach(new_acl, &listener->acl); + dns_acl_detach(&new_acl); + + if (config != NULL) { + get_key_info(config, control, &global_keylist, + &control_keylist); + } + + if (control_keylist != NULL) { + controlkeylist_fromcfg(control_keylist, listener->mctx, + &listener->keys); + register_keys(control, global_keylist, &listener->keys, + listener->mctx, socktext); + } else { + result = get_rndckey(mctx, &listener->keys); + if (result != ISC_R_SUCCESS && control != NULL) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't install keys for " + "command channel %s: %s", + socktext, isc_result_totext(result)); + } + } + + pf = isc_sockaddr_pf(&listener->address); + if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || + (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) || + (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) + { + CHECK(ISC_R_FAMILYNOSUPPORT); + } + +#if 0 + /* XXX: no unix socket support yet */ + if (type == isc_socktype_unix) { + isc_socket_cleanunix(&listener->address, false); + } +#endif + + CHECK(isc_nm_listentcp( + named_g_netmgr, &listener->address, control_newconn, listener, + sizeof(controlconnection_t), 5, NULL, &listener->sock)); +#if 0 + /* XXX: no unix socket support yet */ + if (type == isc_socktype_unix) { + listener->perm = + cfg_obj_asuint32(cfg_tuple_get(control, "perm")); + listener->owner = + cfg_obj_asuint32(cfg_tuple_get(control, "owner")); + listener->group = + cfg_obj_asuint32(cfg_tuple_get(control, "group")); + result = isc_socket_permunix(&listener->address, listener->perm, + listener->owner, listener->group); + } +#endif + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, + "command channel listening on %s", socktext); + *listenerp = listener; + return; + +cleanup: + isc_refcount_decrement(&listener->refs); + listener->exiting = true; + free_listener(listener); + + if (control != NULL) { + cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, + "couldn't add command channel %s: %s", socktext, + isc_result_totext(result)); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, + "couldn't add command channel %s: %s", socktext, + isc_result_totext(result)); + } + + *listenerp = NULL; + + /* XXXDCL return error results? fail hard? */ +} + +isc_result_t +named_controls_configure(named_controls_t *cp, const cfg_obj_t *config, + cfg_aclconfctx_t *aclconfctx) { + controllistener_t *listener = NULL; + controllistenerlist_t new_listeners; + const cfg_obj_t *controlslist = NULL; + const cfg_listelt_t *element, *element2; + char socktext[ISC_SOCKADDR_FORMATSIZE]; + + ISC_LIST_INIT(new_listeners); + + /* + * Get the list of named.conf 'controls' statements. + */ + (void)cfg_map_get(config, "controls", &controlslist); + + /* + * Run through the new control channel list, noting sockets that + * are already being listened on and moving them to the new list. + * + * Identifying duplicate addr/port combinations is left to either + * the underlying config code, or to the bind attempt getting an + * address-in-use error. + */ + if (controlslist != NULL) { + for (element = cfg_list_first(controlslist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *controls = NULL; + const cfg_obj_t *inetcontrols = NULL; + + controls = cfg_listelt_value(element); + (void)cfg_map_get(controls, "inet", &inetcontrols); + if (inetcontrols == NULL) { + continue; + } + + for (element2 = cfg_list_first(inetcontrols); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + const cfg_obj_t *control = NULL; + const cfg_obj_t *obj = NULL; + isc_sockaddr_t addr; + + /* + * The parser handles BIND 8 configuration file + * syntax, so it allows unix phrases as well + * inet phrases with no keys{} clause. + */ + control = cfg_listelt_value(element2); + + obj = cfg_tuple_get(control, "address"); + addr = *cfg_obj_assockaddr(obj); + if (isc_sockaddr_getport(&addr) == 0) { + isc_sockaddr_setport( + &addr, NAMED_CONTROL_PORT); + } + + isc_sockaddr_format(&addr, socktext, + sizeof(socktext)); + + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, + ISC_LOG_DEBUG(9), + "processing control channel %s", + socktext); + + update_listener(cp, &listener, control, config, + &addr, aclconfctx, socktext, + isc_socktype_tcp); + + if (listener != NULL) { + /* + * Remove the listener from the old + * list, so it won't be shut down. + */ + ISC_LIST_UNLINK(cp->listeners, listener, + link); + } else { + /* + * This is a new listener. + */ + add_listener(cp, &listener, control, + config, &addr, aclconfctx, + socktext, + isc_socktype_tcp); + } + + if (listener != NULL) { + ISC_LIST_APPEND(new_listeners, listener, + link); + } + } + } + for (element = cfg_list_first(controlslist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *controls = NULL; + const cfg_obj_t *unixcontrols = NULL; + + controls = cfg_listelt_value(element); + (void)cfg_map_get(controls, "unix", &unixcontrols); + if (unixcontrols == NULL) { + continue; + } + + cfg_obj_log(controls, named_g_lctx, ISC_LOG_ERROR, + "UNIX domain sockets not yet supported"); + return (ISC_R_FAILURE); + +#if 0 + /* XXX: no unix domain socket support in netmgr */ + for (element2 = cfg_list_first(unixcontrols); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + const cfg_obj_t *control = NULL; + const cfg_obj_t *path = NULL; + isc_sockaddr_t addr; + isc_result_t result; + + /* + * The parser handles BIND 8 configuration file + * syntax, so it allows unix phrases as well + * inet phrases with no keys{} clause. + */ + control = cfg_listelt_value(element2); + + path = cfg_tuple_get(control, "path"); + result = isc_sockaddr_frompath( + &addr, cfg_obj_asstring(path)); + if (result != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, + ISC_LOG_DEBUG(9), + "control channel '%s': %s", + cfg_obj_asstring(path), + isc_result_totext(result)); + continue; + } + + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, + ISC_LOG_DEBUG(9), + "processing control channel '%s'", + cfg_obj_asstring(path)); + + update_listener(cp, &listener, control, config, + &addr, aclconfctx, + cfg_obj_asstring(path), + isc_socktype_unix); + + if (listener != NULL) { + /* + * Remove the listener from the old + * list, so it won't be shut down. + */ + ISC_LIST_UNLINK(cp->listeners, listener, + link); + } else { + /* + * This is a new listener. + */ + add_listener(cp, &listener, control, + config, &addr, aclconfctx, + cfg_obj_asstring(path), + isc_socktype_unix); + } + + if (listener != NULL) { + ISC_LIST_APPEND(new_listeners, listener, + link); + } + } +#endif + } + } else { + int i; + + for (i = 0; i < 2; i++) { + isc_sockaddr_t addr; + + if (i == 0) { + struct in_addr localhost; + + if (isc_net_probeipv4() != ISC_R_SUCCESS) { + continue; + } + localhost.s_addr = htonl(INADDR_LOOPBACK); + isc_sockaddr_fromin(&addr, &localhost, 0); + } else { + if (isc_net_probeipv6() != ISC_R_SUCCESS) { + continue; + } + isc_sockaddr_fromin6(&addr, &in6addr_loopback, + 0); + } + isc_sockaddr_setport(&addr, NAMED_CONTROL_PORT); + + isc_sockaddr_format(&addr, socktext, sizeof(socktext)); + + update_listener(cp, &listener, NULL, NULL, &addr, NULL, + socktext, isc_socktype_tcp); + + if (listener != NULL) { + /* + * Remove the listener from the old + * list, so it won't be shut down. + */ + ISC_LIST_UNLINK(cp->listeners, listener, link); + } else { + /* + * This is a new listener. + */ + add_listener(cp, &listener, NULL, NULL, &addr, + NULL, socktext, isc_socktype_tcp); + } + + if (listener != NULL) { + ISC_LIST_APPEND(new_listeners, listener, link); + } + } + } + + /* + * named_control_shutdown() will stop whatever is on the global + * listeners list, which currently only has whatever sockaddrs + * were in the previous configuration (if any) that do not + * remain in the current configuration. + */ + controls_shutdown(cp); + + /* + * Put all of the valid listeners on the listeners list. + * Anything already on listeners in the process of shutting + * down will be taken care of by listen_done(). + */ + ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); + return (ISC_R_SUCCESS); +} + +isc_result_t +named_controls_create(named_server_t *server, named_controls_t **ctrlsp) { + isc_mem_t *mctx = server->mctx; + isc_result_t result; + named_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); + + *controls = (named_controls_t){ + .server = server, + }; + + ISC_LIST_INIT(controls->listeners); + + atomic_init(&controls->shuttingdown, false); + isc_mutex_init(&controls->symtab_lock); + LOCK(&controls->symtab_lock); + result = isccc_cc_createsymtab(&controls->symtab); + UNLOCK(&controls->symtab_lock); + + if (result != ISC_R_SUCCESS) { + isc_mutex_destroy(&controls->symtab_lock); + isc_mem_put(server->mctx, controls, sizeof(*controls)); + return (result); + } + *ctrlsp = controls; + return (ISC_R_SUCCESS); +} + +void +named_controls_destroy(named_controls_t **ctrlsp) { + named_controls_t *controls = *ctrlsp; + *ctrlsp = NULL; + + REQUIRE(ISC_LIST_EMPTY(controls->listeners)); + + LOCK(&controls->symtab_lock); + isccc_symtab_destroy(&controls->symtab); + UNLOCK(&controls->symtab_lock); + isc_mutex_destroy(&controls->symtab_lock); + isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); +} diff --git a/bin/named/dlz_dlopen_driver.c b/bin/named/dlz_dlopen_driver.c new file mode 100644 index 0000000..f636cd2 --- /dev/null +++ b/bin/named/dlz_dlopen_driver.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +static dns_sdlzimplementation_t *dlz_dlopen = NULL; + +typedef struct dlopen_data { + isc_mem_t *mctx; + char *dl_path; + char *dlzname; + uv_lib_t dl_handle; + void *dbdata; + unsigned int flags; + isc_mutex_t lock; + int version; + bool in_configure; + + dlz_dlopen_version_t *dlz_version; + dlz_dlopen_create_t *dlz_create; + dlz_dlopen_findzonedb_t *dlz_findzonedb; + dlz_dlopen_lookup_t *dlz_lookup; + dlz_dlopen_authority_t *dlz_authority; + dlz_dlopen_allnodes_t *dlz_allnodes; + dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr; + dlz_dlopen_newversion_t *dlz_newversion; + dlz_dlopen_closeversion_t *dlz_closeversion; + dlz_dlopen_configure_t *dlz_configure; + dlz_dlopen_ssumatch_t *dlz_ssumatch; + dlz_dlopen_addrdataset_t *dlz_addrdataset; + dlz_dlopen_subrdataset_t *dlz_subrdataset; + dlz_dlopen_delrdataset_t *dlz_delrdataset; + dlz_dlopen_destroy_t *dlz_destroy; +} dlopen_data_t; + +/* Modules can choose whether they are lock-safe or not. */ +#define MAYBE_LOCK(cd) \ + do { \ + if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \ + !cd->in_configure) \ + LOCK(&cd->lock); \ + } while (0) + +#define MAYBE_UNLOCK(cd) \ + do { \ + if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \ + !cd->in_configure) \ + UNLOCK(&cd->lock); \ + } while (0) + +/* + * Log a message at the given level. + */ +static void +dlopen_log(int level, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(level), fmt, ap); + va_end(ap); +} + +/* + * SDLZ methods + */ + +static isc_result_t +dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_allnodes == NULL) { + return (ISC_R_NOPERM); + } + + MAYBE_LOCK(cd); + result = cd->dlz_allnodes(zone, cd->dbdata, allnodes); + MAYBE_UNLOCK(cd); + return (result); +} + +static isc_result_t +dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_allowzonexfr == NULL) { + return (ISC_R_NOPERM); + } + + MAYBE_LOCK(cd); + result = cd->dlz_allowzonexfr(cd->dbdata, name, client); + MAYBE_UNLOCK(cd); + return (result); +} + +static isc_result_t +dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_authority == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + MAYBE_LOCK(cd); + result = cd->dlz_authority(zone, cd->dbdata, lookup); + MAYBE_UNLOCK(cd); + return (result); +} + +static isc_result_t +dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + MAYBE_LOCK(cd); + result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo); + MAYBE_UNLOCK(cd); + return (result); +} + +static isc_result_t +dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg, + void *dbdata, dns_sdlzlookup_t *lookup, + dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + MAYBE_LOCK(cd); + result = cd->dlz_lookup(zone, name, cd->dbdata, lookup, methods, + clientinfo); + MAYBE_UNLOCK(cd); + return (result); +} + +/* + * Load a symbol from the library + */ +static void * +dl_load_symbol(dlopen_data_t *cd, const char *symbol, bool mandatory) { + void *ptr = NULL; + int r = uv_dlsym(&cd->dl_handle, symbol, &ptr); + if (r != 0) { + const char *errmsg = uv_dlerror(&cd->dl_handle); + if (errmsg == NULL) { + errmsg = "returned function pointer is NULL"; + } + if (mandatory) { + dlopen_log(ISC_LOG_ERROR, + "dlz_dlopen: library '%s' is missing " + "required symbol '%s': %s", + cd->dl_path, symbol, errmsg); + } + } + + return (ptr); +} + +static void +dlopen_dlz_destroy(void *driverarg, void *dbdata); + +/* + * Called at startup for each dlopen zone in named.conf + */ +static isc_result_t +dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) { + dlopen_data_t *cd; + isc_mem_t *mctx = NULL; + isc_result_t result = ISC_R_FAILURE; + int r; + + UNUSED(driverarg); + + if (argc < 2) { + dlopen_log(ISC_LOG_ERROR, + "dlz_dlopen driver for '%s' needs a path to " + "the shared library", + dlzname); + return (ISC_R_FAILURE); + } + + isc_mem_create(&mctx); + cd = isc_mem_get(mctx, sizeof(*cd)); + memset(cd, 0, sizeof(*cd)); + + cd->mctx = mctx; + + cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]); + cd->dlzname = isc_mem_strdup(cd->mctx, dlzname); + + /* Initialize the lock */ + isc_mutex_init(&cd->lock); + + r = uv_dlopen(cd->dl_path, &cd->dl_handle); + if (r != 0) { + const char *errmsg = uv_dlerror(&cd->dl_handle); + if (errmsg == NULL) { + errmsg = "unknown error"; + } + dlopen_log(ISC_LOG_ERROR, + "dlz_dlopen failed to open library '%s': %s", + cd->dl_path, errmsg); + result = ISC_R_FAILURE; + goto failed; + } + + /* Find the symbols */ + cd->dlz_version = + (dlz_dlopen_version_t *)dl_load_symbol(cd, "dlz_version", true); + cd->dlz_create = (dlz_dlopen_create_t *)dl_load_symbol(cd, "dlz_create", + true); + cd->dlz_lookup = (dlz_dlopen_lookup_t *)dl_load_symbol(cd, "dlz_lookup", + true); + cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)dl_load_symbol( + cd, "dlz_findzonedb", true); + + if (cd->dlz_create == NULL || cd->dlz_version == NULL || + cd->dlz_lookup == NULL || cd->dlz_findzonedb == NULL) + { + /* We're missing a required symbol */ + result = ISC_R_FAILURE; + goto failed; + } + + cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)dl_load_symbol( + cd, "dlz_allowzonexfr", false); + cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)dl_load_symbol( + cd, "dlz_allnodes", (cd->dlz_allowzonexfr != NULL)); + cd->dlz_authority = (dlz_dlopen_authority_t *)dl_load_symbol( + cd, "dlz_authority", false); + cd->dlz_newversion = (dlz_dlopen_newversion_t *)dl_load_symbol( + cd, "dlz_newversion", false); + cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)dl_load_symbol( + cd, "dlz_closeversion", (cd->dlz_newversion != NULL)); + cd->dlz_configure = (dlz_dlopen_configure_t *)dl_load_symbol( + cd, "dlz_configure", false); + cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)dl_load_symbol( + cd, "dlz_ssumatch", false); + cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)dl_load_symbol( + cd, "dlz_addrdataset", false); + cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)dl_load_symbol( + cd, "dlz_subrdataset", false); + cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)dl_load_symbol( + cd, "dlz_delrdataset", false); + cd->dlz_destroy = (dlz_dlopen_destroy_t *)dl_load_symbol( + cd, "dlz_destroy", false); + + /* Check the version of the API is the same */ + cd->version = cd->dlz_version(&cd->flags); + if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) || + cd->version > DLZ_DLOPEN_VERSION) + { + dlopen_log(ISC_LOG_ERROR, + "dlz_dlopen: %s: incorrect driver API version %d, " + "requires %d", + cd->dl_path, cd->version, DLZ_DLOPEN_VERSION); + result = ISC_R_FAILURE; + goto failed; + } + + /* + * Call the library's create function. Note that this is an + * extended version of dlz create, with the addition of + * named function pointers for helper functions that the + * driver will need. This avoids the need for the backend to + * link the BIND9 libraries + */ + MAYBE_LOCK(cd); + result = cd->dlz_create(dlzname, argc - 1, argv + 1, &cd->dbdata, "log", + dlopen_log, "putrr", dns_sdlz_putrr, + "putnamedrr", dns_sdlz_putnamedrr, + "writeable_zone", dns_dlz_writeablezone, NULL); + MAYBE_UNLOCK(cd); + if (result != ISC_R_SUCCESS) { + goto failed; + } + + *dbdata = cd; + + return (ISC_R_SUCCESS); + +failed: + dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname); + + dlopen_dlz_destroy(NULL, cd); + + return (result); +} + +/* + * Called when bind is shutting down + */ +static void +dlopen_dlz_destroy(void *driverarg, void *dbdata) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + + UNUSED(driverarg); + + if (cd->dlz_destroy && cd->dbdata) { + MAYBE_LOCK(cd); + cd->dlz_destroy(cd->dbdata); + MAYBE_UNLOCK(cd); + } + + uv_dlclose(&cd->dl_handle); + isc_mutex_destroy(&cd->lock); + isc_mem_free(cd->mctx, cd->dl_path); + isc_mem_free(cd->mctx, cd->dlzname); + isc_mem_putanddetach(&cd->mctx, cd, sizeof(*cd)); +} + +/* + * Called to start a transaction + */ +static isc_result_t +dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata, + void **versionp) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_newversion == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + MAYBE_LOCK(cd); + result = cd->dlz_newversion(zone, cd->dbdata, versionp); + MAYBE_UNLOCK(cd); + return (result); +} + +/* + * Called to end a transaction + */ +static void +dlopen_dlz_closeversion(const char *zone, bool commit, void *driverarg, + void *dbdata, void **versionp) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + + UNUSED(driverarg); + + if (cd->dlz_newversion == NULL) { + *versionp = NULL; + return; + } + + MAYBE_LOCK(cd); + cd->dlz_closeversion(zone, commit, cd->dbdata, versionp); + MAYBE_UNLOCK(cd); +} + +/* + * Called on startup to configure any writeable zones + */ +static isc_result_t +dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb, void *driverarg, + void *dbdata) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_configure == NULL) { + return (ISC_R_SUCCESS); + } + + MAYBE_LOCK(cd); + cd->in_configure = true; + result = cd->dlz_configure(view, dlzdb, cd->dbdata); + cd->in_configure = false; + MAYBE_UNLOCK(cd); + + return (result); +} + +/* + * Check for authority to change a name. + */ +static bool +dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, + const char *type, const char *key, uint32_t keydatalen, + unsigned char *keydata, void *driverarg, void *dbdata) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + bool ret; + + UNUSED(driverarg); + + if (cd->dlz_ssumatch == NULL) { + return (false); + } + + MAYBE_LOCK(cd); + ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen, + keydata, cd->dbdata); + MAYBE_UNLOCK(cd); + + return (ret); +} + +/* + * Add an rdataset. + */ +static isc_result_t +dlopen_dlz_addrdataset(const char *name, const char *rdatastr, void *driverarg, + void *dbdata, void *version) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_addrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + MAYBE_LOCK(cd); + result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version); + MAYBE_UNLOCK(cd); + + return (result); +} + +/* + * Subtract an rdataset. + */ +static isc_result_t +dlopen_dlz_subrdataset(const char *name, const char *rdatastr, void *driverarg, + void *dbdata, void *version) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_subrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + MAYBE_LOCK(cd); + result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version); + MAYBE_UNLOCK(cd); + + return (result); +} + +/* + * Delete a rdataset. + */ +static isc_result_t +dlopen_dlz_delrdataset(const char *name, const char *type, void *driverarg, + void *dbdata, void *version) { + dlopen_data_t *cd = (dlopen_data_t *)dbdata; + isc_result_t result; + + UNUSED(driverarg); + + if (cd->dlz_delrdataset == NULL) { + return (ISC_R_NOTIMPLEMENTED); + } + + MAYBE_LOCK(cd); + result = cd->dlz_delrdataset(name, type, cd->dbdata, version); + MAYBE_UNLOCK(cd); + + return (result); +} + +static dns_sdlzmethods_t dlz_dlopen_methods = { + dlopen_dlz_create, dlopen_dlz_destroy, dlopen_dlz_findzonedb, + dlopen_dlz_lookup, dlopen_dlz_authority, dlopen_dlz_allnodes, + dlopen_dlz_allowzonexfr, dlopen_dlz_newversion, dlopen_dlz_closeversion, + dlopen_dlz_configure, dlopen_dlz_ssumatch, dlopen_dlz_addrdataset, + dlopen_dlz_subrdataset, dlopen_dlz_delrdataset +}; + +/* + * Register driver with BIND + */ +isc_result_t +dlz_dlopen_init(isc_mem_t *mctx) { + isc_result_t result; + + dlopen_log(2, "Registering DLZ_dlopen driver"); + + result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + mctx, &dlz_dlopen); + + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR("dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +/* + * Unregister the driver + */ +void +dlz_dlopen_clear(void) { + dlopen_log(2, "Unregistering DLZ_dlopen driver"); + if (dlz_dlopen != NULL) { + dns_sdlzunregister(&dlz_dlopen); + } +} diff --git a/bin/named/fuzz.c b/bin/named/fuzz.c new file mode 100644 index 0000000..fb0d56f --- /dev/null +++ b/bin/named/fuzz.c @@ -0,0 +1,782 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include + +#include + +#ifdef ENABLE_AFL +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* + * We are using pthreads directly because we might be using it with + * unthreaded version of BIND, where all thread functions are + * mocks. Since AFL for now only works on Linux it's not a problem. + */ +static pthread_cond_t cond; +static pthread_mutex_t mutex; +static bool ready; + +/* + * In "client:" mode, this thread reads fuzzed query messages from AFL + * from standard input and sends it to named's listening port (DNS) that + * is passed in the -A client:
: option. It can be used to + * test named from the client side. + */ +static void * +fuzz_thread_client(void *arg) { + char *host; + char *port; + struct sockaddr_in servaddr; + int sockfd; + void *buf; + + UNUSED(arg); + + /* + * Parse named -A argument in the "address:port" syntax. Due to + * the syntax used, this only supports IPv4 addresses. + */ + host = strdup(named_g_fuzz_addr); + RUNTIME_CHECK(host != NULL); + + port = strchr(host, ':'); + RUNTIME_CHECK(port != NULL); + *port = 0; + ++port; + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1); + servaddr.sin_port = htons(atoi(port)); + + free(host); + + /* + * Wait for named to start. This is set in run_server() in the + * named thread. + */ + while (!named_g_run_done) { + usleep(10000); + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + RUNTIME_CHECK(sockfd != -1); + + buf = malloc(65536); + RUNTIME_CHECK(buf != NULL); + + /* + * Processing fuzzed packets 100,000 times before shutting down + * the app. + */ +#ifdef __AFL_LOOP + for (int loop = 0; loop < 100000; loop++) { +#else /* ifdef __AFL_LOOP */ + { +#endif /* ifdef __AFL_LOOP */ + ssize_t length; + ssize_t sent; + + length = read(0, buf, 65536); + if (length <= 0) { + usleep(1000000); + goto next; + } + + /* + * Ignore packets that are larger than 4096 bytes. + */ + if (length > 4096) { + /* + * AFL_CMIN doesn't support persistent mode, so + * shutdown the server. + */ + if (getenv("AFL_CMIN")) { + free(buf); + close(sockfd); + named_server_flushonshutdown(named_g_server, + false); + isc_app_shutdown(); + return (NULL); + } + raise(SIGSTOP); + goto next; + } + + RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); + + ready = false; + + sent = sendto(sockfd, buf, length, 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + RUNTIME_CHECK(sent == length); + + /* + * Read the reply message from named to unclog it. Don't + * bother if there isn't a reply. + */ + (void)recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL); + + while (!ready) { + pthread_cond_wait(&cond, &mutex); + } + + RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); + next:; + } + + free(buf); + close(sockfd); + + named_server_flushonshutdown(named_g_server, false); + isc_app_shutdown(); + + return (NULL); +} + +/* + * In "resolver:" mode, this thread reads fuzzed reply messages from AFL + * from standard input. It also sets up a listener as a remote + * authoritative server and sends a driver query to the client side of + * named(resolver). When named(resolver) connects to this authoritative + * server, this thread writes the fuzzed reply message from AFL to it. + * + * -A resolver:::: + * + * Here, : is where named(resolver) is listening on. + * : is where the thread is supposed to setup the + * authoritative server. This address should be configured via the root + * zone to be the authoritiative server for aaaaaaaaaa.example. + * + * named(resolver) when being fuzzed will not cache answers. + */ +static void * +fuzz_thread_resolver(void *arg) { + char *sqtype, *shost, *sport, *rhost, *rport; + struct sockaddr_in servaddr, recaddr, recvaddr; + /* + * Query for aaaaaaaaaa.example./A in wire format with RD=1, + * EDNS and DO=1. 0x88, 0x0c at the start is the ID field which + * will be updated for each query. + */ + char respacket[] = { 0x88, 0x0c, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x0a, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x07, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }; + /* + * Response for example./DNSKEY in wire format. Note that RRSIGs + * were generated with this DNSKEY that are used as seeds for + * AFL in the DNSSEC fuzzing job. So the DNSKEY content of this + * message must not change, or the corresponding RRSIGs will + * have to be updated. 0x8d, 0xf6 at the start is the ID field + * which will be made to match the query. + */ + const uint8_t dnskey_wf[] = { + 0x8d, 0xf6, 0x84, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x01, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x00, 0x00, 0x30, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x30, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x08, 0x01, 0x00, 0x03, + 0x08, 0x03, 0x01, 0x00, 0x01, 0xbd, 0x81, 0xdc, 0x7f, 0x16, + 0xd4, 0x81, 0x7c, 0x1f, 0x9f, 0x6a, 0x68, 0xdd, 0xd4, 0xda, + 0x48, 0xd9, 0x1c, 0xbd, 0xa6, 0x46, 0x1a, 0xf0, 0xb4, 0xb9, + 0xec, 0x3d, 0x6c, 0x0b, 0x57, 0xc7, 0xd6, 0x54, 0x66, 0xe6, + 0x6c, 0xd5, 0x90, 0x3a, 0x78, 0x7d, 0x7f, 0x78, 0x80, 0xa2, + 0x89, 0x61, 0x6d, 0x8a, 0x2b, 0xcd, 0x0a, 0x77, 0x7a, 0xad, + 0xc9, 0x61, 0x53, 0x53, 0x8c, 0x99, 0x72, 0x86, 0x14, 0x74, + 0x9c, 0x49, 0x2a, 0x47, 0x23, 0xf7, 0x02, 0x07, 0x73, 0x1c, + 0x5c, 0x2e, 0xb4, 0x9a, 0xa4, 0xd7, 0x98, 0x42, 0xc3, 0xd2, + 0xfe, 0xbf, 0xf3, 0xb3, 0x6a, 0x52, 0x92, 0xd5, 0xfa, 0x47, + 0x00, 0xe3, 0xd9, 0x59, 0x31, 0x95, 0x48, 0x40, 0xfc, 0x06, + 0x73, 0x90, 0xc6, 0x73, 0x96, 0xba, 0x29, 0x91, 0xe2, 0xac, + 0xa3, 0xa5, 0x6d, 0x91, 0x6d, 0x52, 0xb9, 0x34, 0xba, 0x68, + 0x4f, 0xad, 0xf0, 0xc3, 0xf3, 0x1d, 0x6d, 0x61, 0x76, 0xe5, + 0x3d, 0xa3, 0x9b, 0x2a, 0x0c, 0x92, 0xb3, 0x78, 0x6b, 0xf1, + 0x20, 0xd6, 0x90, 0xb7, 0xac, 0xe2, 0xf8, 0x2b, 0x94, 0x10, + 0x79, 0xce, 0xa8, 0x60, 0x42, 0xea, 0x6a, 0x18, 0x2f, 0xc0, + 0xd8, 0x05, 0x0a, 0x3b, 0x06, 0x0f, 0x02, 0x7e, 0xff, 0x33, + 0x46, 0xee, 0xb6, 0x21, 0x25, 0x90, 0x63, 0x4b, 0x3b, 0x5e, + 0xb2, 0x72, 0x3a, 0xcb, 0x91, 0x41, 0xf4, 0x20, 0x50, 0x78, + 0x1c, 0x93, 0x95, 0xda, 0xfa, 0xae, 0x85, 0xc5, 0xd7, 0x6b, + 0x92, 0x0c, 0x70, 0x6b, 0xe4, 0xb7, 0x29, 0x3a, 0x2e, 0x18, + 0x88, 0x82, 0x33, 0x7c, 0xa8, 0xea, 0xb8, 0x31, 0x8f, 0xaf, + 0x50, 0xc5, 0x9c, 0x08, 0x56, 0x8f, 0x09, 0x76, 0x4e, 0xdf, + 0x97, 0x75, 0x9d, 0x00, 0x52, 0x7f, 0xdb, 0xec, 0x30, 0xcb, + 0x1c, 0x4c, 0x2a, 0x21, 0x93, 0xc4, 0x6d, 0x85, 0xa9, 0x40, + 0x3b, 0xc0, 0x0c, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x2c, 0x01, 0x1b, 0x00, 0x30, 0x08, 0x01, 0x00, 0x00, 0x01, + 0x2c, 0x67, 0x74, 0x85, 0x80, 0x58, 0xb3, 0xc5, 0x17, 0x36, + 0x90, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, + 0x45, 0xac, 0xd3, 0x82, 0x69, 0xf3, 0x10, 0x3a, 0x97, 0x2c, + 0x6a, 0xa9, 0x78, 0x99, 0xea, 0xb0, 0xcc, 0xf7, 0xaf, 0x33, + 0x51, 0x5b, 0xdf, 0x77, 0x04, 0x18, 0x14, 0x99, 0x61, 0xeb, + 0x8d, 0x76, 0x3f, 0xd1, 0x71, 0x14, 0x43, 0x80, 0x53, 0xc2, + 0x3b, 0x9f, 0x09, 0x4f, 0xb3, 0x51, 0x04, 0x89, 0x0e, 0xc8, + 0x54, 0x12, 0xcd, 0x07, 0x20, 0xbe, 0x94, 0xc2, 0xda, 0x99, + 0xdd, 0x1e, 0xf8, 0xb0, 0x84, 0x2e, 0xf9, 0x19, 0x35, 0x36, + 0xf5, 0xd0, 0x5d, 0x82, 0x18, 0x74, 0xa0, 0x00, 0xb6, 0x15, + 0x57, 0x40, 0x5f, 0x78, 0x2d, 0x27, 0xac, 0xc7, 0x8a, 0x29, + 0x55, 0xa9, 0xcd, 0xbc, 0xf7, 0x3e, 0xff, 0xae, 0x1a, 0x5a, + 0x1d, 0xac, 0x0d, 0x78, 0x0e, 0x08, 0x33, 0x6c, 0x59, 0x70, + 0x40, 0xb9, 0x65, 0xbd, 0x35, 0xbb, 0x9a, 0x70, 0xdc, 0x93, + 0x66, 0xb0, 0xef, 0xfe, 0xf0, 0x32, 0xa6, 0xee, 0xb7, 0x03, + 0x89, 0xa2, 0x4d, 0xe0, 0xf1, 0x20, 0xdf, 0x39, 0xe8, 0xe3, + 0xcc, 0x95, 0xe9, 0x9a, 0xad, 0xbf, 0xbd, 0x7c, 0xf7, 0xd7, + 0xde, 0x47, 0x9e, 0xf6, 0x17, 0xbb, 0x84, 0xa9, 0xed, 0xf2, + 0x45, 0x61, 0x6d, 0x13, 0x0b, 0x06, 0x29, 0x50, 0xde, 0xfd, + 0x42, 0xb0, 0x66, 0x2c, 0x1c, 0x2b, 0x63, 0xcb, 0x4e, 0xb9, + 0x31, 0xc4, 0xea, 0xd2, 0x07, 0x3a, 0x08, 0x79, 0x19, 0x4b, + 0x4c, 0x50, 0x97, 0x02, 0xd7, 0x26, 0x41, 0x2f, 0xdd, 0x57, + 0xaa, 0xb0, 0xa0, 0x21, 0x4e, 0x74, 0xb6, 0x97, 0x4b, 0x8b, + 0x09, 0x9c, 0x3d, 0x29, 0xfb, 0x12, 0x27, 0x47, 0x8f, 0xb8, + 0xc5, 0x8e, 0x65, 0xcd, 0xca, 0x2f, 0xba, 0xf5, 0x3e, 0xec, + 0x56, 0xc3, 0xc9, 0xa1, 0x62, 0x7d, 0xf2, 0x9f, 0x90, 0x16, + 0x1d, 0xbf, 0x97, 0x28, 0xe1, 0x92, 0xb1, 0x53, 0xab, 0xc4, + 0xe0, 0x99, 0xbb, 0x19, 0x90, 0x7c, 0x00, 0x00, 0x29, 0x10, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 + }; + + int sockfd; + int listenfd; + int loop; + uint16_t qtype; + char *buf, *rbuf; + char *nameptr; + unsigned int i; + uint8_t llen; + uint64_t seed; + + UNUSED(arg); + + /* + * Parse named -A argument in the "qtype:saddress:sport:raddress:rport" + * syntax. Due to the syntax used, this only supports IPv4 addresses. + */ + sqtype = strdup(named_g_fuzz_addr); + RUNTIME_CHECK(sqtype != NULL); + + shost = strchr(sqtype, ':'); + RUNTIME_CHECK(shost != NULL); + *shost = 0; + shost++; + + sport = strchr(shost, ':'); + RUNTIME_CHECK(sport != NULL); + *sport = 0; + sport++; + + rhost = strchr(sport, ':'); + RUNTIME_CHECK(rhost != NULL); + *rhost = 0; + rhost++; + + rport = strchr(rhost, ':'); + RUNTIME_CHECK(rport != NULL); + *rport = 0; + rport++; + + /* + * Patch in the qtype into the question section of respacket. + */ + qtype = atoi(sqtype); + respacket[32] = (qtype >> 8) & 0xff; + respacket[33] = qtype & 0xff; + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1); + servaddr.sin_port = htons(atoi(sport)); + + memset(&recaddr, 0, sizeof(recaddr)); + recaddr.sin_family = AF_INET; + RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1); + recaddr.sin_port = htons(atoi(rport)); + + free(sqtype); + + /* + * Wait for named to start. This is set in run_server() in the + * named thread. + */ + while (!named_g_run_done) { + usleep(10000); + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + RUNTIME_CHECK(sockfd != -1); + + listenfd = socket(AF_INET, SOCK_DGRAM, 0); + RUNTIME_CHECK(listenfd != -1); + + RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr, + sizeof(struct sockaddr_in)) == 0); + + buf = malloc(65536); + rbuf = malloc(65536); + RUNTIME_CHECK(buf != NULL); + RUNTIME_CHECK(rbuf != NULL); + + seed = 42; + + /* + * Processing fuzzed packets 100,000 times before shutting down + * the app. + */ + for (loop = 0; loop < 100000; loop++) { + ssize_t length; + ssize_t sent; + unsigned short id; + socklen_t socklen; + + memset(buf, 0, 12); + length = read(0, buf, 65536); + if (length <= 0) { + usleep(1000000); + continue; + } + + if (length > 4096) { + if (getenv("AFL_CMIN")) { + free(buf); + free(rbuf); + close(sockfd); + close(listenfd); + named_server_flushonshutdown(named_g_server, + false); + isc_app_shutdown(); + return (NULL); + } + raise(SIGSTOP); + continue; + } + + if (length < 12) { + length = 12; + } + + RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); + + ready = false; + + /* Use a unique query ID. */ + seed = 1664525 * seed + 1013904223; + id = seed & 0xffff; + respacket[0] = (id >> 8) & 0xff; + respacket[1] = id & 0xff; + + /* + * Flush any pending data on the authoritative server. + */ + socklen = sizeof(recvaddr); + (void)recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT, + (struct sockaddr *)&recvaddr, &socklen); + + /* + * Send a fixed client query to named(resolver) of + * aaaaaaaaaa.example./A. This is the starting query + * driver. + */ + sent = sendto(sockfd, respacket, sizeof(respacket), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + RUNTIME_CHECK(sent == sizeof(respacket)); + + /* + * named(resolver) will process the query above and send + * an upstream query to the authoritative server. We + * handle that here as the upstream authoritative server + * on listenfd. + */ + socklen = sizeof(recvaddr); + sent = recvfrom(listenfd, rbuf, 65536, 0, + (struct sockaddr *)&recvaddr, &socklen); + RUNTIME_CHECK(sent > 0); + + /* + * Copy QID and set QR so that response is always + * accepted by named(resolver). + */ + buf[0] = rbuf[0]; + buf[1] = rbuf[1]; + buf[2] |= 0x80; + + /* + * NOTE: We are not copying the QNAME or setting + * rcode=NOERROR each time. So the resolver may fail the + * client query (driver) / wander due to this. AA flag + * may also not be set based on the contents of the AFL + * fuzzed packet. + */ + + /* + * A hack - set QTYPE to the one from query so that we + * can easily share packets between instances. If we + * write over something else we'll get FORMERR anyway. + */ + + /* Skip DNS header to get to the name */ + nameptr = buf + 12; + + /* Skip the name to get to the qtype */ + i = 0; + while (((llen = nameptr[i]) != 0) && (i < 255) && + (((nameptr + i + 1 + llen) - buf) < length)) + { + i += 1 + llen; + } + + if (i <= 255) { + nameptr += 1 + i; + /* Patch the qtype */ + if ((nameptr - buf) < (length - 2)) { + *nameptr++ = (qtype >> 8) & 0xff; + *nameptr++ = qtype & 0xff; + } + /* Patch the qclass */ + if ((nameptr - buf) < (length - 2)) { + *nameptr++ = 0; + *nameptr++ = 1; + } + } + + /* + * Send the reply to named(resolver). + */ + sent = sendto(listenfd, buf, length, 0, + (struct sockaddr *)&recvaddr, sizeof(recvaddr)); + RUNTIME_CHECK(sent == length); + + /* We might get additional questions here (e.g. for CNAME). */ + for (;;) { + fd_set fds; + struct timeval tv; + int rv; + int max; + + FD_ZERO(&fds); + FD_SET(listenfd, &fds); + FD_SET(sockfd, &fds); + tv.tv_sec = 10; + tv.tv_usec = 0; + max = (listenfd > sockfd ? listenfd : sockfd) + 1; + + rv = select(max, &fds, NULL, NULL, &tv); + RUNTIME_CHECK(rv > 0); + + if (FD_ISSET(sockfd, &fds)) { + /* + * It's the reply from named(resolver) + * to the client(query driver), so we're + * done. + */ + (void)recvfrom(sockfd, buf, 65536, 0, NULL, + NULL); + break; + } + + /* + * We've got additional question (eg. due to + * CNAME). Bounce it - setting QR flag and + * NOERROR rcode and sending it back. + */ + length = recvfrom(listenfd, buf, 65536, 0, + (struct sockaddr *)&recvaddr, + &socklen); + + /* + * If this is a DNSKEY query, send the DNSKEY, + * otherwise, bounce the query. + */ + + /* Skip DNS header to get to the name */ + nameptr = buf + 12; + + /* Skip the name to get to the qtype */ + i = 0; + while (((llen = nameptr[i]) != 0) && (i < 255) && + (((nameptr + i + 1 + llen) - buf) < length)) + { + i += 1 + llen; + } + + if (i <= 255) { + nameptr += 1 + i; + /* + * Patch in the DNSKEY reply without + * touching the ID field. Note that we + * don't compare the name in the + * question section in the query, but we + * don't expect to receive any query for + * type DNSKEY but for the name + * "example." + */ + if ((nameptr - buf) < (length - 2)) { + uint8_t hb, lb; + hb = *nameptr++; + lb = *nameptr++; + qtype = (hb << 8) | lb; + + if (qtype == 48) { + memmove(buf + 2, dnskey_wf + 2, + sizeof(dnskey_wf) - 2); + length = sizeof(dnskey_wf); + } + } + } + + buf[2] |= 0x80; + buf[3] &= 0xF0; + sent = sendto(listenfd, buf, length, 0, + (struct sockaddr *)&recvaddr, + sizeof(recvaddr)); + RUNTIME_CHECK(sent == length); + } + + while (!ready) { + pthread_cond_wait(&cond, &mutex); + } + + RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); + } + + free(buf); + free(rbuf); + close(sockfd); + close(listenfd); + named_server_flushonshutdown(named_g_server, false); + isc_app_shutdown(); + +#ifdef __AFL_LOOP + /* + * This is here just for the signature, that's how AFL detects + * if it's a 'persistent mode' binary. It has to occur somewhere + * in the file, that's all. < wpk_> AFL checks the binary for + * this signature ("##SIG_AFL_PERSISTENT##") and runs the binary + * in persistent mode if it's present. + */ + __AFL_LOOP(0); +#endif /* ifdef __AFL_LOOP */ + + return (NULL); +} + +/* + * In "tcp:", "http:" and "rndc:" modes, this thread reads fuzzed query + * blobs from AFL from standard input and sends it to the corresponding + * TCP listening port of named (port 53 DNS, or the HTTP statistics + * channels listener or the rndc port) that is passed in the -A + * :
: option. It can be used to test named from the + * client side. + */ +static void * +fuzz_thread_tcp(void *arg) { + char *host; + char *port; + struct sockaddr_in servaddr; + int sockfd; + char *buf; + int loop; + + UNUSED(arg); + + /* + * Parse named -A argument in the "address:port" syntax. Due to + * the syntax used, this only supports IPv4 addresses. + */ + host = strdup(named_g_fuzz_addr); + RUNTIME_CHECK(host != NULL); + + port = strchr(host, ':'); + RUNTIME_CHECK(port != NULL); + *port = 0; + ++port; + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1); + servaddr.sin_port = htons(atoi(port)); + + free(host); + + /* + * Wait for named to start. This is set in run_server() in the + * named thread. + */ + while (!named_g_run_done) { + usleep(10000); + } + + buf = malloc(65539); + RUNTIME_CHECK(buf != NULL); + + /* + * Processing fuzzed packets 100,000 times before shutting down + * the app. + */ + for (loop = 0; loop < 100000; loop++) { + ssize_t length; + ssize_t sent; + int yes; + int r; + + if (named_g_fuzz_type == isc_fuzz_tcpclient) { + /* + * To fuzz DNS TCP client we have to put 16-bit + * message length preceding the start of packet. + */ + length = read(0, buf + 2, 65535); + buf[0] = (length >> 8) & 0xff; + buf[1] = length & 0xff; + length += 2; + } else { + /* + * Other types of TCP clients such as HTTP, etc. + */ + length = read(0, buf, 65535); + } + if (length <= 0) { + usleep(1000000); + continue; + } + if (named_g_fuzz_type == isc_fuzz_http) { + /* + * This guarantees that the request will be + * processed. + */ + INSIST(length <= 65535); + buf[length++] = '\r'; + buf[length++] = '\n'; + buf[length++] = '\r'; + buf[length++] = '\n'; + } + + RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); + + ready = false; + yes = 1; + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + RUNTIME_CHECK(sockfd != -1); + RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == 0); + + do { + r = connect(sockfd, (struct sockaddr *)&servaddr, + sizeof(servaddr)); + if (r != 0) { + usleep(10000); + } + } while (r != 0); + + /* + * Send the fuzzed query blob to the target server. + */ + sent = write(sockfd, buf, length); + RUNTIME_CHECK(sent == length); + + close(sockfd); + + while (!ready) { + pthread_cond_wait(&cond, &mutex); + } + + RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); + } + + free(buf); + close(sockfd); + named_server_flushonshutdown(named_g_server, false); + isc_app_shutdown(); + + return (NULL); +} + +#endif /* ENABLE_AFL */ + +/* + * named has finished processing a message and has sent the + * reply. Signal the fuzz thread using the condition variable, to read + * and process the next item from AFL. + */ +void +named_fuzz_notify(void) { +#ifdef ENABLE_AFL + if (getenv("AFL_CMIN")) { + named_server_flushonshutdown(named_g_server, false); + isc_app_shutdown(); + return; + } + + raise(SIGSTOP); + + RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); + + ready = true; + + RUNTIME_CHECK(pthread_cond_signal(&cond) == 0); + RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); +#endif /* ENABLE_AFL */ +} + +void +named_fuzz_setup(void) { +#ifdef ENABLE_AFL + if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) { + pthread_t thread; + void *(fn) = NULL; + + switch (named_g_fuzz_type) { + case isc_fuzz_client: + fn = fuzz_thread_client; + break; + + case isc_fuzz_http: + case isc_fuzz_tcpclient: + case isc_fuzz_rndc: + fn = fuzz_thread_tcp; + break; + + case isc_fuzz_resolver: + fn = fuzz_thread_resolver; + break; + + default: + RUNTIME_CHECK(fn != NULL); + } + + RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0); + RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0); + RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0); + } +#endif /* ENABLE_AFL */ +} diff --git a/bin/named/geoip.c b/bin/named/geoip.c new file mode 100644 index 0000000..0ba4ef0 --- /dev/null +++ b/bin/named/geoip.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#if defined(HAVE_GEOIP2) +#include +#endif /* if defined(HAVE_GEOIP2) */ + +#include +#include +#include +#include + +#include + +#include +#include + +static dns_geoip_databases_t geoip_table; + +#if defined(HAVE_GEOIP2) +static MMDB_s geoip_country, geoip_city, geoip_as, geoip_isp, geoip_domain; + +static MMDB_s * +open_geoip2(const char *dir, const char *dbfile, MMDB_s *mmdb) { + char pathbuf[PATH_MAX]; + unsigned int n; + int ret; + + n = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, dbfile); + if (n >= sizeof(pathbuf)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "GeoIP2 database '%s/%s': path too long", dir, + dbfile); + return (NULL); + } + + ret = MMDB_open(pathbuf, MMDB_MODE_MMAP, mmdb); + if (ret == MMDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "opened GeoIP2 database '%s'", pathbuf); + return (mmdb); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "unable to open GeoIP2 database '%s' (status %d)", + pathbuf, ret); + + return (NULL); +} +#endif /* HAVE_GEOIP2 */ + +void +named_geoip_init(void) { +#if defined(HAVE_GEOIP2) + if (named_g_geoip == NULL) { + named_g_geoip = &geoip_table; + } +#else /* if defined(HAVE_GEOIP2) */ + return; +#endif /* if defined(HAVE_GEOIP2) */ +} + +void +named_geoip_load(char *dir) { +#if defined(HAVE_GEOIP2) + REQUIRE(dir != NULL); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "looking for GeoIP2 databases in '%s'", dir); + + named_g_geoip->country = open_geoip2(dir, "GeoIP2-Country.mmdb", + &geoip_country); + if (named_g_geoip->country == NULL) { + named_g_geoip->country = open_geoip2( + dir, "GeoLite2-Country.mmdb", &geoip_country); + } + + named_g_geoip->city = open_geoip2(dir, "GeoIP2-City.mmdb", &geoip_city); + if (named_g_geoip->city == NULL) { + named_g_geoip->city = open_geoip2(dir, "GeoLite2-City.mmdb", + &geoip_city); + } + + named_g_geoip->as = open_geoip2(dir, "GeoIP2-ASN.mmdb", &geoip_as); + if (named_g_geoip->as == NULL) { + named_g_geoip->as = open_geoip2(dir, "GeoLite2-ASN.mmdb", + &geoip_as); + } + + named_g_geoip->isp = open_geoip2(dir, "GeoIP2-ISP.mmdb", &geoip_isp); + named_g_geoip->domain = open_geoip2(dir, "GeoIP2-Domain.mmdb", + &geoip_domain); +#else /* if defined(HAVE_GEOIP2) */ + UNUSED(dir); + + return; +#endif /* if defined(HAVE_GEOIP2) */ +} + +void +named_geoip_unload(void) { +#ifdef HAVE_GEOIP2 + if (named_g_geoip->country != NULL) { + MMDB_close(named_g_geoip->country); + named_g_geoip->country = NULL; + } + if (named_g_geoip->city != NULL) { + MMDB_close(named_g_geoip->city); + named_g_geoip->city = NULL; + } + if (named_g_geoip->as != NULL) { + MMDB_close(named_g_geoip->as); + named_g_geoip->as = NULL; + } + if (named_g_geoip->isp != NULL) { + MMDB_close(named_g_geoip->isp); + named_g_geoip->isp = NULL; + } + if (named_g_geoip->domain != NULL) { + MMDB_close(named_g_geoip->domain); + named_g_geoip->domain = NULL; + } +#endif /* ifdef HAVE_GEOIP2 */ +} + +void +named_geoip_shutdown(void) { +#ifdef HAVE_GEOIP2 + named_geoip_unload(); +#endif /* HAVE_GEOIP2 */ +} diff --git a/bin/named/include/dlz/dlz_dlopen_driver.h b/bin/named/include/dlz/dlz_dlopen_driver.h new file mode 100644 index 0000000..64d6388 --- /dev/null +++ b/bin/named/include/dlz/dlz_dlopen_driver.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +isc_result_t +dlz_dlopen_init(isc_mem_t *mctx); + +void +dlz_dlopen_clear(void); diff --git a/bin/named/include/named/builtin.h b/bin/named/include/named/builtin.h new file mode 100644 index 0000000..fbfc599 --- /dev/null +++ b/bin/named/include/named/builtin.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +isc_result_t +named_builtin_init(void); + +void +named_builtin_deinit(void); diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h new file mode 100644 index 0000000..d9c5aa3 --- /dev/null +++ b/bin/named/include/named/config.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +#include +#include + +#include + +#define DEFAULT_IANA_ROOT_ZONE_PRIMARIES "_default_iana_root_zone_primaries" + +isc_result_t +named_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf); + +const char * +named_config_getdefault(void); + +isc_result_t +named_config_get(cfg_obj_t const *const *maps, const char *name, + const cfg_obj_t **obj); + +isc_result_t +named_checknames_get(const cfg_obj_t **maps, const char *const names[], + const cfg_obj_t **obj); + +int +named_config_listcount(const cfg_obj_t *list); + +isc_result_t +named_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass, + dns_rdataclass_t *classp); + +isc_result_t +named_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype, + dns_rdatatype_t *typep); + +dns_zonetype_t +named_config_getzonetype(const cfg_obj_t *zonetypeobj); + +isc_result_t +named_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list, + in_port_t defport, isc_mem_t *mctx, + isc_sockaddr_t **addrsp, uint32_t *countp); + +void +named_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp, + uint32_t count); + +isc_result_t +named_config_getremotesdef(const cfg_obj_t *cctx, const char *list, + const char *name, const cfg_obj_t **ret); + +isc_result_t +named_config_getipandkeylist(const cfg_obj_t *config, const char *listtype, + const cfg_obj_t *list, isc_mem_t *mctx, + dns_ipkeylist_t *ipkl); + +isc_result_t +named_config_getport(const cfg_obj_t *config, const char *type, + in_port_t *portp); + +isc_result_t +named_config_getkeyalgorithm(const char *str, const dns_name_t **name, + uint16_t *digestbits); +isc_result_t +named_config_getkeyalgorithm2(const char *str, const dns_name_t **name, + unsigned int *typep, uint16_t *digestbits); diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h new file mode 100644 index 0000000..29b5677 --- /dev/null +++ b/bin/named/include/named/control.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file + * \brief + * The name server command channel. + */ + +#include + +#include + +#include +#include + +#define NAMED_CONTROL_PORT 953 + +#define NAMED_COMMAND_STOP "stop" +#define NAMED_COMMAND_HALT "halt" +#define NAMED_COMMAND_RELOAD "reload" +#define NAMED_COMMAND_RECONFIG "reconfig" +#define NAMED_COMMAND_REFRESH "refresh" +#define NAMED_COMMAND_RETRANSFER "retransfer" +#define NAMED_COMMAND_DUMPSTATS "stats" +#define NAMED_COMMAND_QUERYLOG "querylog" +#define NAMED_COMMAND_DUMPDB "dumpdb" +#define NAMED_COMMAND_SECROOTS "secroots" +#define NAMED_COMMAND_TRACE "trace" +#define NAMED_COMMAND_NOTRACE "notrace" +#define NAMED_COMMAND_FLUSH "flush" +#define NAMED_COMMAND_FLUSHNAME "flushname" +#define NAMED_COMMAND_FLUSHTREE "flushtree" +#define NAMED_COMMAND_STATUS "status" +#define NAMED_COMMAND_TSIGLIST "tsig-list" +#define NAMED_COMMAND_TSIGDELETE "tsig-delete" +#define NAMED_COMMAND_FREEZE "freeze" +#define NAMED_COMMAND_UNFREEZE "unfreeze" +#define NAMED_COMMAND_THAW "thaw" +#define NAMED_COMMAND_TIMERPOKE "timerpoke" +#define NAMED_COMMAND_RECURSING "recursing" +#define NAMED_COMMAND_NULL "null" +#define NAMED_COMMAND_NOTIFY "notify" +#define NAMED_COMMAND_VALIDATION "validation" +#define NAMED_COMMAND_SCAN "scan" +#define NAMED_COMMAND_SIGN "sign" +#define NAMED_COMMAND_LOADKEYS "loadkeys" +#define NAMED_COMMAND_ADDZONE "addzone" +#define NAMED_COMMAND_MODZONE "modzone" +#define NAMED_COMMAND_DELZONE "delzone" +#define NAMED_COMMAND_SHOWZONE "showzone" +#define NAMED_COMMAND_SYNC "sync" +#define NAMED_COMMAND_SIGNING "signing" +#define NAMED_COMMAND_DNSSEC "dnssec" +#define NAMED_COMMAND_ZONESTATUS "zonestatus" +#define NAMED_COMMAND_NTA "nta" +#define NAMED_COMMAND_TESTGEN "testgen" +#define NAMED_COMMAND_MKEYS "managed-keys" +#define NAMED_COMMAND_DNSTAPREOPEN "dnstap-reopen" +#define NAMED_COMMAND_DNSTAP "dnstap" +#define NAMED_COMMAND_TCPTIMEOUTS "tcp-timeouts" +#define NAMED_COMMAND_SERVESTALE "serve-stale" + +isc_result_t +named_controls_create(named_server_t *server, named_controls_t **ctrlsp); +/*%< + * Create an initial, empty set of command channels for 'server'. + */ + +void +named_controls_destroy(named_controls_t **ctrlsp); +/*%< + * Destroy a set of command channels. + * + * Requires: + * Shutdown of the channels has completed. + */ + +isc_result_t +named_controls_configure(named_controls_t *controls, const cfg_obj_t *config, + cfg_aclconfctx_t *aclconfctx); +/*%< + * Configure zero or more command channels into 'controls' + * as defined in the configuration parse tree 'config'. + * The channels will evaluate ACLs in the context of + * 'aclconfctx'. + */ + +void +named_controls_shutdown(named_controls_t *controls); +/*%< + * Initiate shutdown of all the command channels in 'controls'. + */ + +isc_result_t +named_control_docommand(isccc_sexpr_t *message, bool readonly, + isc_buffer_t **text); diff --git a/bin/named/include/named/fuzz.h b/bin/named/include/named/fuzz.h new file mode 100644 index 0000000..69af8da --- /dev/null +++ b/bin/named/include/named/fuzz.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include + +#pragma once + +void +named_fuzz_notify(void); + +void +named_fuzz_setup(void); diff --git a/bin/named/include/named/geoip.h b/bin/named/include/named/geoip.h new file mode 100644 index 0000000..d1852ef --- /dev/null +++ b/bin/named/include/named/geoip.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +extern dns_geoip_databases_t *named_g_geoip; + +void +named_geoip_init(void); + +void +named_geoip_load(char *dir); + +void +named_geoip_unload(void); + +void +named_geoip_shutdown(void); diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h new file mode 100644 index 0000000..c65e933 --- /dev/null +++ b/bin/named/include/named/globals.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#undef EXTERN +#undef INIT +#ifdef NAMED_MAIN +#define EXTERN +#define INIT(v) = (v) +#else /* ifdef NAMED_MAIN */ +#define EXTERN extern +#define INIT(v) +#endif /* ifdef NAMED_MAIN */ + +#ifndef NAMED_RUN_PID_DIR +#define NAMED_RUN_PID_DIR 1 +#endif /* ifndef NAMED_RUN_PID_DIR */ + +EXTERN isc_mem_t *named_g_mctx INIT(NULL); +EXTERN unsigned int named_g_cpus INIT(0); +EXTERN unsigned int named_g_udpdisp INIT(0); +EXTERN isc_taskmgr_t *named_g_taskmgr INIT(NULL); +EXTERN dns_dispatchmgr_t *named_g_dispatchmgr INIT(NULL); +EXTERN unsigned int named_g_cpus_detected INIT(1); + +#ifdef ENABLE_AFL +EXTERN bool named_g_run_done INIT(false); +#endif /* ifdef ENABLE_AFL */ +/* + * XXXRTH We're going to want multiple timer managers eventually. One + * for really short timers, another for client timers, and one + * for zone timers. + */ +EXTERN isc_timermgr_t *named_g_timermgr INIT(NULL); +EXTERN isc_nm_t *named_g_netmgr INIT(NULL); +EXTERN cfg_parser_t *named_g_parser INIT(NULL); +EXTERN cfg_parser_t *named_g_addparser INIT(NULL); +EXTERN const char *named_g_version INIT(PACKAGE_VERSION); +EXTERN const char *named_g_product INIT(PACKAGE_NAME); +EXTERN const char *named_g_description INIT(PACKAGE_DESCRIPTION); +EXTERN const char *named_g_srcid INIT(PACKAGE_SRCID); +EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS); +EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER); +EXTERN in_port_t named_g_port INIT(0); +EXTERN in_port_t named_g_tlsport INIT(0); +EXTERN in_port_t named_g_httpsport INIT(0); +EXTERN in_port_t named_g_httpport INIT(0); + +EXTERN in_port_t named_g_http_listener_clients INIT(0); +EXTERN in_port_t named_g_http_streams_per_conn INIT(0); + +EXTERN named_server_t *named_g_server INIT(NULL); + +/* + * Logging. + */ +EXTERN isc_log_t *named_g_lctx INIT(NULL); +EXTERN isc_logcategory_t *named_g_categories INIT(NULL); +EXTERN isc_logmodule_t *named_g_modules INIT(NULL); +EXTERN unsigned int named_g_debuglevel INIT(0); + +/* + * Current configuration information. + */ +EXTERN cfg_obj_t *named_g_config INIT(NULL); +EXTERN const cfg_obj_t *named_g_defaults INIT(NULL); +EXTERN const char *named_g_conffile INIT(NAMED_SYSCONFDIR "/named.conf"); +EXTERN const char *named_g_defaultbindkeys INIT(NAMED_SYSCONFDIR "/bind.keys"); +EXTERN const char *named_g_keyfile INIT(NAMED_SYSCONFDIR "/rndc.key"); + +EXTERN dns_tsigkey_t *named_g_sessionkey INIT(NULL); +EXTERN dns_name_t named_g_sessionkeyname; +EXTERN bool named_g_conffileset INIT(false); +EXTERN cfg_aclconfctx_t *named_g_aclconfctx INIT(NULL); + +/* + * Initial resource limits. + */ +EXTERN isc_resourcevalue_t named_g_initstacksize INIT(0); +EXTERN isc_resourcevalue_t named_g_initdatasize INIT(0); +EXTERN isc_resourcevalue_t named_g_initcoresize INIT(0); +EXTERN isc_resourcevalue_t named_g_initopenfiles INIT(0); + +/* + * Misc. + */ +EXTERN bool named_g_coreok INIT(true); +EXTERN const char *named_g_chrootdir INIT(NULL); +EXTERN bool named_g_foreground INIT(false); +EXTERN bool named_g_logstderr INIT(false); +EXTERN bool named_g_nosyslog INIT(false); +EXTERN const char *named_g_logfile INIT(NULL); + +EXTERN const char *named_g_defaultsessionkeyfile INIT(NAMED_LOCALSTATEDIR + "/run/named/" + "session.key"); +EXTERN const char *named_g_defaultlockfile INIT(NAMED_LOCALSTATEDIR "/run/" + "named/" + "named." + "lock"); +EXTERN bool named_g_forcelock INIT(false); + +#if NAMED_RUN_PID_DIR +EXTERN const char *named_g_defaultpidfile INIT(NAMED_LOCALSTATEDIR "/run/named/" + "named.pid"); +#else /* if NAMED_RUN_PID_DIR */ +EXTERN const char *named_g_defaultpidfile INIT(NAMED_LOCALSTATEDIR "/run/" + "named.pid"); +#endif /* if NAMED_RUN_PID_DIR */ + +EXTERN const char *named_g_username INIT(NULL); + +EXTERN const char *named_g_engine INIT(NULL); + +EXTERN isc_time_t named_g_boottime; +EXTERN isc_time_t named_g_configtime; +EXTERN bool named_g_memstatistics INIT(false); +EXTERN bool named_g_keepstderr INIT(false); + +EXTERN unsigned int named_g_tat_interval INIT(24 * 3600); +EXTERN unsigned int named_g_maxcachesize INIT(0); + +#if defined(HAVE_GEOIP2) +EXTERN dns_geoip_databases_t *named_g_geoip INIT(NULL); +#endif /* if defined(HAVE_GEOIP2) */ + +EXTERN const char *named_g_fuzz_addr INIT(NULL); +EXTERN isc_fuzztype_t named_g_fuzz_type INIT(isc_fuzz_none); + +EXTERN dns_acl_t *named_g_mapped INIT(NULL); + +#undef EXTERN +#undef INIT diff --git a/bin/named/include/named/log.h b/bin/named/include/named/log.h new file mode 100644 index 0000000..f18e93a --- /dev/null +++ b/bin/named/include/named/log.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include + +#include /* Required for named_g_(categories|modules). */ + +/* Unused slot 0. */ +#define NAMED_LOGCATEGORY_UNMATCHED (&named_g_categories[1]) + +/* + * Backwards compatibility. + */ +#define NAMED_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL + +#define NAMED_LOGMODULE_MAIN (&named_g_modules[0]) +#define NAMED_LOGMODULE_SERVER (&named_g_modules[1]) +#define NAMED_LOGMODULE_CONTROL (&named_g_modules[2]) + +isc_result_t +named_log_init(bool safe); +/*% + * Initialize the logging system and set up an initial default + * logging default configuration that will be used until the + * config file has been read. + * + * If 'safe' is true, use a default configuration that refrains + * from opening files. This is to avoid creating log files + * as root. + */ + +void +named_log_setdefaultchannels(isc_logconfig_t *lcfg); +/*% + * Set up logging channels according to the named defaults, which + * may differ from the logging library defaults. Currently, + * this just means setting up default_debug. + */ + +void +named_log_setsafechannels(isc_logconfig_t *lcfg); +/*% + * Like named_log_setdefaultchannels(), but omits any logging to files. + */ + +void +named_log_setdefaultsslkeylogfile(isc_logconfig_t *lcfg); +/*% + * If the SSLKEYLOGFILE environment variable is set, sets up a default + * logging channel for writing TLS pre-master secrets to the path stored + * in that environment variable (for debugging purposes). + */ + +isc_result_t +named_log_setdefaultcategory(isc_logconfig_t *lcfg); +/*% + * Set up "category default" to go to the right places. + */ + +isc_result_t +named_log_setunmatchedcategory(isc_logconfig_t *lcfg); +/*% + * Set up "category unmatched" to go to the right places. + */ + +void +named_log_shutdown(void); diff --git a/bin/named/include/named/logconf.h b/bin/named/include/named/logconf.h new file mode 100644 index 0000000..65add46 --- /dev/null +++ b/bin/named/include/named/logconf.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +isc_result_t +named_logconfig(isc_logconfig_t *logconf, const cfg_obj_t *logstmt); +/*%< + * Set up the logging configuration in '*logconf' according to + * the named.conf data in 'logstmt'. + */ diff --git a/bin/named/include/named/main.h b/bin/named/include/named/main.h new file mode 100644 index 0000000..42fd138 --- /dev/null +++ b/bin/named/include/named/main.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include + +/*! \file */ + +#ifdef ISC_MAIN_HOOK +#define main(argc, argv) bindmain(argc, argv) +#endif /* ifdef ISC_MAIN_HOOK */ + +/* + * Commandline arguments for named; + */ +#define NAMED_MAIN_ARGS "46A:c:Cd:D:E:fFgL:M:m:n:N:p:sS:t:T:U:u:vVx:X:" + +noreturn void +named_main_earlyfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +named_main_earlywarning(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +named_main_setmemstats(const char *); diff --git a/bin/named/include/named/os.h b/bin/named/include/named/os.h new file mode 100644 index 0000000..0f7c1c5 --- /dev/null +++ b/bin/named/include/named/os.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include + +void +named_os_init(const char *progname); + +void +named_os_daemonize(void); + +void +named_os_opendevnull(void); + +void +named_os_closedevnull(void); + +void +named_os_chroot(const char *root); + +void +named_os_inituserinfo(const char *username); + +void +named_os_changeuser(void); + +uid_t +ns_os_uid(void); + +void +named_os_adjustnofile(void); + +void +named_os_minprivs(void); + +FILE * +named_os_openfile(const char *filename, mode_t mode, bool switch_user); + +void +named_os_writepidfile(const char *filename, bool first_time); + +bool +named_os_issingleton(const char *filename); + +void +named_os_shutdown(void); + +void +named_os_shutdownmsg(char *command, isc_buffer_t *text); + +void +named_os_tzset(void); + +void +named_os_started(void); + +const char * +named_os_uname(void); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h new file mode 100644 index 0000000..075e2ec --- /dev/null +++ b/bin/named/include/named/server.h @@ -0,0 +1,396 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define NAMED_EVENTCLASS ISC_EVENTCLASS(0x4E43) +#define NAMED_EVENT_RELOAD (NAMED_EVENTCLASS + 0) +#define NAMED_EVENT_DELZONE (NAMED_EVENTCLASS + 1) +#define NAMED_EVENT_COMMAND (NAMED_EVENTCLASS + 2) +#define NAMED_EVENT_TATSEND (NAMED_EVENTCLASS + 3) + +/*% + * Name server state. Better here than in lots of separate global variables. + */ +struct named_server { + unsigned int magic; + isc_mem_t *mctx; + + ns_server_t *sctx; + + isc_task_t *task; + + char *statsfile; /*%< Statistics file name */ + char *dumpfile; /*%< Dump file name */ + char *secrootsfile; /*%< Secroots file name */ + char *bindkeysfile; /*%< bind.keys file name + * */ + char *recfile; /*%< Recursive file name */ + bool version_set; /*%< User has set version + * */ + char *version; /*%< User-specified version */ + bool hostname_set; /*%< User has set hostname + * */ + char *hostname; /*%< User-specified hostname + * */ + + /* Server data structures. */ + dns_loadmgr_t *loadmgr; + dns_zonemgr_t *zonemgr; + dns_viewlist_t viewlist; + dns_kasplist_t kasplist; + ns_interfacemgr_t *interfacemgr; + dns_db_t *in_roothints; + + isc_timer_t *interface_timer; + isc_timer_t *heartbeat_timer; + isc_timer_t *pps_timer; + isc_timer_t *tat_timer; + + uint32_t interface_interval; + uint32_t heartbeat_interval; + + atomic_int reload_status; + + bool flushonshutdown; + + named_cachelist_t cachelist; /*%< Possibly shared caches + * */ + isc_stats_t *zonestats; /*% Zone management stats */ + isc_stats_t *resolverstats; /*% Resolver stats */ + isc_stats_t *sockstats; /*%< Socket stats */ + + named_controls_t *controls; /*%< Control channels */ + unsigned int dispatchgen; + named_dispatchlist_t dispatches; + + named_statschannellist_t statschannels; + + dst_key_t *sessionkey; + char *session_keyfile; + dns_name_t *session_keyname; + unsigned int session_keyalg; + uint16_t session_keybits; + bool interface_auto; + unsigned char secret[32]; /*%< Server Cookie Secret */ + ns_cookiealg_t cookiealg; + + dns_dtenv_t *dtenv; /*%< Dnstap environment */ + + char *lockfile; + + isc_tlsctx_cache_t *tlsctx_server_cache; + isc_tlsctx_cache_t *tlsctx_client_cache; +}; + +#define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R') +#define NAMED_SERVER_VALID(s) ISC_MAGIC_VALID(s, NAMED_SERVER_MAGIC) + +void +named_server_create(isc_mem_t *mctx, named_server_t **serverp); +/*%< + * Create a server object with default settings. + * This function either succeeds or causes the program to exit + * with a fatal error. + */ + +void +named_server_destroy(named_server_t **serverp); +/*%< + * Destroy a server object, freeing its memory. + */ + +void +named_server_reloadwanted(named_server_t *server); +/*%< + * Inform a server that a reload is wanted. This function + * may be called asynchronously, from outside the server's task. + * If a reload is already scheduled or in progress, the call + * is ignored. + */ + +void +named_server_scan_interfaces(named_server_t *server); +/*%< + * Trigger a interface scan. + * Must only be called when running under server->task. + */ + +void +named_server_flushonshutdown(named_server_t *server, bool flush); +/*%< + * Inform the server that the zones should be flushed to disk on shutdown. + */ + +isc_result_t +named_server_reloadcommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); +/*%< + * Act on a "reload" command from the command channel. + */ + +isc_result_t +named_server_reconfigcommand(named_server_t *server); +/*%< + * Act on a "reconfig" command from the command channel. + */ + +isc_result_t +named_server_notifycommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); +/*%< + * Act on a "notify" command from the command channel. + */ + +isc_result_t +named_server_refreshcommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); +/*%< + * Act on a "refresh" command from the command channel. + */ + +isc_result_t +named_server_retransfercommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); +/*%< + * Act on a "retransfer" command from the command channel. + */ + +isc_result_t +named_server_togglequerylog(named_server_t *server, isc_lex_t *lex); +/*%< + * Enable/disable logging of queries. (Takes "yes" or "no" argument, + * but can also be used as a toggle for backward comptibility.) + */ + +/*% + * Save the current NTAs for all views to files. + */ +isc_result_t +named_server_saventa(named_server_t *server); + +/*% + * Load NTAs for all views from files. + */ +isc_result_t +named_server_loadnta(named_server_t *server); + +/*% + * Dump the current statistics to the statistics file. + */ +isc_result_t +named_server_dumpstats(named_server_t *server); + +/*% + * Dump the current cache to the dump file. + */ +isc_result_t +named_server_dumpdb(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Dump the current security roots to the secroots file. + */ +isc_result_t +named_server_dumpsecroots(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Change or increment the server debug level. + */ +isc_result_t +named_server_setdebuglevel(named_server_t *server, isc_lex_t *lex); + +/*% + * Flush the server's cache(s) + */ +isc_result_t +named_server_flushcache(named_server_t *server, isc_lex_t *lex); + +/*% + * Flush a particular name from the server's cache. If 'tree' is false, + * also flush the name from the ADB and badcache. If 'tree' is true, also + * flush all the names under the specified name. + */ +isc_result_t +named_server_flushnode(named_server_t *server, isc_lex_t *lex, bool tree); + +/*% + * Report the server's status. + */ +isc_result_t +named_server_status(named_server_t *server, isc_buffer_t **text); + +/*% + * Report a list of dynamic and static tsig keys, per view. + */ +isc_result_t +named_server_tsiglist(named_server_t *server, isc_buffer_t **text); + +/*% + * Delete a specific key (with optional view). + */ +isc_result_t +named_server_tsigdelete(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Enable or disable updates for a zone. + */ +isc_result_t +named_server_freeze(named_server_t *server, bool freeze, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Dump zone updates to disk, optionally removing the journal file + */ +isc_result_t +named_server_sync(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Update a zone's DNSKEY set from the key repository. If + * the command that triggered the call to this function was "sign", + * then force a full signing of the zone. If it was "loadkeys", + * then don't sign the zone; any needed changes to signatures can + * take place incrementally. + */ +isc_result_t +named_server_rekey(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Dump the current recursive queries. + */ +isc_result_t +named_server_dumprecursing(named_server_t *server); + +/*% + * Maintain a list of dispatches that require reserved ports. + */ +void +named_add_reserved_dispatch(named_server_t *server, const isc_sockaddr_t *addr); + +/*% + * Enable or disable dnssec validation. + */ +isc_result_t +named_server_validation(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Add a zone to a running process, or modify an existing zone + */ +isc_result_t +named_server_changezone(named_server_t *server, char *command, + isc_buffer_t **text); + +/*% + * Deletes a zone from a running process + */ +isc_result_t +named_server_delzone(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Show current configuration for a given zone + */ +isc_result_t +named_server_showzone(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Lists the status of the signing records for a given zone. + */ +isc_result_t +named_server_signing(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Lists the DNSSEC status for a given zone. + */ +isc_result_t +named_server_dnssec(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Lists status information for a given zone (e.g., name, type, files, + * load time, expiry, etc). + */ +isc_result_t +named_server_zonestatus(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Adds/updates a Negative Trust Anchor (NTA) for a specified name and + * duration, in a particular view if specified, or in all views. + */ +isc_result_t +named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly, + isc_buffer_t **text); + +/*% + * Generates a test sequence that is only for use in system tests. The + * argument is the size of required output in bytes. + */ +isc_result_t +named_server_testgen(isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Force fefresh or print status for managed keys zones. + */ +isc_result_t +named_server_mkeys(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Close and reopen DNSTAP output file. + */ +isc_result_t +named_server_dnstap(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); + +/*% + * Display or update tcp-{initial,idle,keepalive,advertised}-timeout options. + */ +isc_result_t +named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text); + +/*% + * Control whether stale answers are served or not when configured in + * named.conf. + */ +isc_result_t +named_server_servestale(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text); diff --git a/bin/named/include/named/smf_globals.h b/bin/named/include/named/smf_globals.h new file mode 100644 index 0000000..b052822 --- /dev/null +++ b/bin/named/include/named/smf_globals.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#include + +#undef EXTERN +#undef INIT +#ifdef NAMED_MAIN +#define EXTERN +#define INIT(v) = (v) +#else /* ifdef NAMED_MAIN */ +#define EXTERN extern +#define INIT(v) +#endif /* ifdef NAMED_MAIN */ + +EXTERN unsigned int named_smf_got_instance INIT(0); +EXTERN unsigned int named_smf_chroot INIT(0); +EXTERN unsigned int named_smf_want_disable INIT(0); + +isc_result_t +named_smf_add_message(isc_buffer_t **text); +isc_result_t +named_smf_get_instance(char **name, int debug, isc_mem_t *mctx); + +#undef EXTERN +#undef INIT diff --git a/bin/named/include/named/statschannel.h b/bin/named/include/named/statschannel.h new file mode 100644 index 0000000..8240dc1 --- /dev/null +++ b/bin/named/include/named/statschannel.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file + * \brief + * The statistics channels built-in the name server. + */ + +#include + +#include +#include + +#define NAMED_STATSCHANNEL_HTTPPORT 80 + +isc_result_t +named_statschannels_configure(named_server_t *server, const cfg_obj_t *config, + cfg_aclconfctx_t *aclconfctx); +/*%< + * [Re]configure the statistics channels. + * + * If it is no longer there but was previously configured, destroy + * it here. + * + * If the IP address or port has changed, destroy the old server + * and create a new one. + */ + +void +named_statschannels_shutdown(named_server_t *server); +/*%< + * Initiate shutdown of all the statistics channel listeners. + */ + +isc_result_t +named_stats_dump(named_server_t *server, FILE *fp); +/*%< + * Dump statistics counters managed by the server to the file fp. + */ diff --git a/bin/named/include/named/tkeyconf.h b/bin/named/include/named/tkeyconf.h new file mode 100644 index 0000000..79639d6 --- /dev/null +++ b/bin/named/include/named/tkeyconf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +named_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx, + dns_tkeyctx_t **tctxp); +/*%< + * Create a TKEY context and configure it, including the default DH key + * and default domain, according to 'options'. + * + * Requires: + *\li 'cfg' is a valid configuration options object. + *\li 'mctx' is not NULL + *\li 'tctx' is not NULL + *\li '*tctx' is NULL + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_NOMEMORY + */ + +ISC_LANG_ENDDECLS diff --git a/bin/named/include/named/transportconf.h b/bin/named/include/named/transportconf.h new file mode 100644 index 0000000..1e472ff --- /dev/null +++ b/bin/named/include/named/transportconf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +#include + +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_transport_list_t **listp); +/*%< + * Create a list of transport objects (DoT or DoH) and configure them + * according to 'key-file', 'cert-file', 'ca-file' or 'hostname' + * statements. + * + * Requires: + * \li 'config' is not NULL. + * \li 'vconfig' is not NULL. + * \li 'mctx' is not NULL + * \li 'listp' is not NULL, and '*listp' is NULL + * + */ + +ISC_LANG_ENDDECLS diff --git a/bin/named/include/named/tsigconf.h b/bin/named/include/named/tsigconf.h new file mode 100644 index 0000000..32a0120 --- /dev/null +++ b/bin/named/include/named/tsigconf.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +named_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_tsig_keyring_t **ringp); +/*%< + * Create a TSIG key ring and configure it according to the 'key' + * statements in the global and view configuration objects. + * + * Requires: + * \li 'config' is not NULL. + * \li 'vconfig' is not NULL. + * \li 'mctx' is not NULL + * \li 'ringp' is not NULL, and '*ringp' is NULL + * + * Returns: + * \li ISC_R_SUCCESS + * \li ISC_R_NOMEMORY + */ + +ISC_LANG_ENDDECLS diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h new file mode 100644 index 0000000..585c141 --- /dev/null +++ b/bin/named/include/named/types.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +typedef struct named_cache named_cache_t; +typedef ISC_LIST(named_cache_t) named_cachelist_t; +typedef struct named_server named_server_t; +typedef struct named_xmld named_xmld_t; +typedef struct named_xmldmgr named_xmldmgr_t; +typedef struct named_controls named_controls_t; +typedef struct named_dispatch named_dispatch_t; +typedef ISC_LIST(named_dispatch_t) named_dispatchlist_t; +typedef struct named_statschannel named_statschannel_t; +typedef ISC_LIST(named_statschannel_t) named_statschannellist_t; + +/*% + * Used for server->reload_status as printed by `rndc status` + */ +typedef enum { + NAMED_RELOAD_DONE, + NAMED_RELOAD_IN_PROGRESS, + NAMED_RELOAD_FAILED, +} named_reload_t; diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h new file mode 100644 index 0000000..387d8a1 --- /dev/null +++ b/bin/named/include/named/zoneconf.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include + +#include +#include + +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, + const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, + dns_kasplist_t *kasplist, dns_zone_t *zone, + dns_zone_t *raw); +/*%< + * Configure or reconfigure a zone according to the named.conf + * data. + * + * The zone origin is not configured, it is assumed to have been set + * at zone creation time. + * + * Require: + * \li 'ac' to point to an initialized cfg_aclconfctx_t. + * \li 'kasplist' to be initialized. + * \li 'zone' to be initialized. + */ + +bool +named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig); +/*%< + * If 'zone' can be safely reconfigured according to the configuration + * data in 'zconfig', return true. If the configuration data is so + * different from the current zone state that the zone needs to be destroyed + * and recreated, return false. + */ + +bool +named_zone_inlinesigning(const cfg_obj_t *zconfig); +/*%< + * Determine if zone uses inline-signing. This is true if inline-signing + * is set to yes. + */ + +isc_result_t +named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone, + dns_rdataclass_t rdclass, dns_name_t *name); +/*%> + * configure a DLZ zone, setting up the database methods and calling + * postload to load the origin values + * + * Require: + * \li 'dlzdatabase' to be a valid dlz database + * \li 'zone' to be initialized. + * \li 'rdclass' to be a valid rdataclass + * \li 'name' to be a valid zone origin name + */ + +ISC_LANG_ENDDECLS diff --git a/bin/named/log.c b/bin/named/log.c new file mode 100644 index 0000000..6efea02 --- /dev/null +++ b/bin/named/log.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include + +#include + +#include + +#include + +#include + +#ifndef ISC_FACILITY +#define ISC_FACILITY LOG_DAEMON +#endif /* ifndef ISC_FACILITY */ + +/*% + * When adding a new category, be sure to add the appropriate + * \#define to and to update the list in + * bin/check/check-tool.c. + */ +static isc_logcategory_t categories[] = { { "", 0 }, + { "unmatched", 0 }, + { NULL, 0 } }; + +/*% + * When adding a new module, be sure to add the appropriate + * \#define to . + */ +static isc_logmodule_t modules[] = { + { "main", 0 }, { "server", 0 }, { "control", 0 }, { NULL, 0 } +}; + +isc_result_t +named_log_init(bool safe) { + isc_result_t result; + isc_logconfig_t *lcfg = NULL; + + named_g_categories = categories; + named_g_modules = modules; + + /* + * Setup a logging context. + */ + isc_log_create(named_g_mctx, &named_g_lctx, &lcfg); + + /* + * named-checktool.c:setup_logging() needs to be kept in sync. + */ + isc_log_registercategories(named_g_lctx, named_g_categories); + isc_log_registermodules(named_g_lctx, named_g_modules); + isc_log_setcontext(named_g_lctx); + dns_log_init(named_g_lctx); + dns_log_setcontext(named_g_lctx); + cfg_log_init(named_g_lctx); + ns_log_init(named_g_lctx); + ns_log_setcontext(named_g_lctx); + + if (safe) { + named_log_setsafechannels(lcfg); + } else { + named_log_setdefaultchannels(lcfg); + } + + result = named_log_setdefaultcategory(lcfg); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + named_log_setdefaultsslkeylogfile(lcfg); + + return (ISC_R_SUCCESS); + +cleanup: + isc_log_destroy(&named_g_lctx); + isc_log_setcontext(NULL); + dns_log_setcontext(NULL); + + return (result); +} + +void +named_log_setdefaultchannels(isc_logconfig_t *lcfg) { + isc_logdestination_t destination; + + /* + * By default, the logging library makes "default_debug" log to + * stderr. In BIND, we want to override this and log to named.run + * instead, unless the -g option was given. + */ + if (!named_g_logstderr) { + destination.file.stream = NULL; + destination.file.name = "named.run"; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILE, + ISC_LOG_DYNAMIC, &destination, + ISC_LOG_PRINTTIME | ISC_LOG_DEBUGONLY); + } + + if (named_g_logfile != NULL) { + destination.file.stream = NULL; + destination.file.name = named_g_logfile; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(lcfg, "default_logfile", ISC_LOG_TOFILE, + ISC_LOG_DYNAMIC, &destination, + ISC_LOG_PRINTTIME | + ISC_LOG_PRINTCATEGORY | + ISC_LOG_PRINTLEVEL); + } + +#if ISC_FACILITY != LOG_DAEMON + destination.facility = ISC_FACILITY; + isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, + ISC_LOG_INFO, &destination, 0); +#endif /* if ISC_FACILITY != LOG_DAEMON */ + + /* + * Set the initial debug level. + */ + isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); +} + +void +named_log_setsafechannels(isc_logconfig_t *lcfg) { + isc_logdestination_t destination; + + if (!named_g_logstderr) { + isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TONULL, + ISC_LOG_DYNAMIC, NULL, 0); + + /* + * Setting the debug level to zero should get the output + * discarded a bit faster. + */ + isc_log_setdebuglevel(named_g_lctx, 0); + } else { + isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); + } + + if (named_g_logfile != NULL) { + destination.file.stream = NULL; + destination.file.name = named_g_logfile; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + isc_log_createchannel(lcfg, "default_logfile", ISC_LOG_TOFILE, + ISC_LOG_DYNAMIC, &destination, + ISC_LOG_PRINTTIME | + ISC_LOG_PRINTCATEGORY | + ISC_LOG_PRINTLEVEL); + } + +#if ISC_FACILITY != LOG_DAEMON + destination.facility = ISC_FACILITY; + isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, + ISC_LOG_INFO, &destination, 0); +#endif /* if ISC_FACILITY != LOG_DAEMON */ +} + +/* + * If the SSLKEYLOGFILE environment variable is set, TLS pre-master secrets are + * logged (for debugging purposes) to the file whose path is provided in that + * variable. Set up a default logging channel which maintains up to 10 files + * containing TLS pre-master secrets, each up to 100 MB in size. If the + * SSLKEYLOGFILE environment variable is set to the string "config", suppress + * creation of the default channel, allowing custom logging channel + * configuration for TLS pre-master secrets to be provided via the "logging" + * stanza in the configuration file. + */ +void +named_log_setdefaultsslkeylogfile(isc_logconfig_t *lcfg) { + const char *sslkeylogfile_path = getenv("SSLKEYLOGFILE"); + isc_logdestination_t destination = { + .file = { + .name = sslkeylogfile_path, + .versions = 10, + .suffix = isc_log_rollsuffix_timestamp, + .maximum_size = 100 * 1024 * 1024, + }, + }; + isc_result_t result; + + if (sslkeylogfile_path == NULL || + strcmp(sslkeylogfile_path, "config") == 0) + { + return; + } + + isc_log_createchannel(lcfg, "default_sslkeylogfile", ISC_LOG_TOFILE, + ISC_LOG_INFO, &destination, 0); + result = isc_log_usechannel(lcfg, "default_sslkeylogfile", + ISC_LOGCATEGORY_SSLKEYLOG, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +isc_result_t +named_log_setdefaultcategory(isc_logconfig_t *lcfg) { + isc_result_t result = ISC_R_SUCCESS; + + result = isc_log_usechannel(lcfg, "default_debug", + ISC_LOGCATEGORY_DEFAULT, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (!named_g_logstderr) { + if (named_g_logfile != NULL) { + result = isc_log_usechannel(lcfg, "default_logfile", + ISC_LOGCATEGORY_DEFAULT, + NULL); + } else if (!named_g_nosyslog) { + result = isc_log_usechannel(lcfg, "default_syslog", + ISC_LOGCATEGORY_DEFAULT, + NULL); + } + } + +cleanup: + return (result); +} + +isc_result_t +named_log_setunmatchedcategory(isc_logconfig_t *lcfg) { + isc_result_t result; + + result = isc_log_usechannel(lcfg, "null", NAMED_LOGCATEGORY_UNMATCHED, + NULL); + return (result); +} + +void +named_log_shutdown(void) { + isc_log_destroy(&named_g_lctx); + isc_log_setcontext(NULL); + dns_log_setcontext(NULL); +} diff --git a/bin/named/logconf.c b/bin/named/logconf.c new file mode 100644 index 0000000..5fd0904 --- /dev/null +++ b/bin/named/logconf.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +/*% + * Set up a logging category according to the named.conf data + * in 'ccat' and add it to 'logconfig'. + */ +static isc_result_t +category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) { + isc_result_t result; + const char *catname; + isc_logcategory_t *category; + isc_logmodule_t *module; + const cfg_obj_t *destinations = NULL; + const cfg_listelt_t *element = NULL; + + catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name")); + category = isc_log_categorybyname(named_g_lctx, catname); + if (category == NULL) { + cfg_obj_log(ccat, named_g_lctx, ISC_LOG_ERROR, + "unknown logging category '%s' ignored", catname); + /* + * Allow further processing by returning success. + */ + return (ISC_R_SUCCESS); + } + + if (logconfig == NULL) { + return (ISC_R_SUCCESS); + } + + module = NULL; + + destinations = cfg_tuple_get(ccat, "destinations"); + for (element = cfg_list_first(destinations); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *channel = cfg_listelt_value(element); + const char *channelname = cfg_obj_asstring(channel); + + result = isc_log_usechannel(logconfig, channelname, category, + module); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, CFG_LOGCATEGORY_CONFIG, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "logging channel '%s': %s", channelname, + isc_result_totext(result)); + return (result); + } + } + return (ISC_R_SUCCESS); +} + +/*% + * Set up a logging channel according to the named.conf data + * in 'cchan' and add it to 'logconfig'. + */ +static isc_result_t +channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig) { + isc_result_t result = ISC_R_SUCCESS; + isc_logdestination_t dest; + unsigned int type; + unsigned int flags = 0; + int level; + const char *channelname; + const cfg_obj_t *fileobj = NULL; + const cfg_obj_t *syslogobj = NULL; + const cfg_obj_t *nullobj = NULL; + const cfg_obj_t *stderrobj = NULL; + const cfg_obj_t *severity = NULL; + int i; + + channelname = cfg_obj_asstring(cfg_map_getname(channel)); + + (void)cfg_map_get(channel, "file", &fileobj); + (void)cfg_map_get(channel, "syslog", &syslogobj); + (void)cfg_map_get(channel, "null", &nullobj); + (void)cfg_map_get(channel, "stderr", &stderrobj); + + i = 0; + if (fileobj != NULL) { + i++; + } + if (syslogobj != NULL) { + i++; + } + if (nullobj != NULL) { + i++; + } + if (stderrobj != NULL) { + i++; + } + + if (i != 1) { + cfg_obj_log(channel, named_g_lctx, ISC_LOG_ERROR, + "channel '%s': exactly one of file, syslog, " + "null, and stderr must be present", + channelname); + return (ISC_R_FAILURE); + } + + type = ISC_LOG_TONULL; + + if (fileobj != NULL) { + const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file"); + const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size"); + const cfg_obj_t *versionsobj = cfg_tuple_get(fileobj, + "versions"); + const cfg_obj_t *suffixobj = cfg_tuple_get(fileobj, "suffix"); + int32_t versions = ISC_LOG_ROLLNEVER; + isc_log_rollsuffix_t suffix = isc_log_rollsuffix_increment; + isc_offset_t size = 0; + uint64_t maxoffset; + + /* + * isc_offset_t is a signed integer type, so the maximum + * value is all 1s except for the MSB. + */ + switch (sizeof(isc_offset_t)) { + case 4: + maxoffset = 0x7fffffffULL; + break; + case 8: + maxoffset = 0x7fffffffffffffffULL; + break; + default: + UNREACHABLE(); + } + + type = ISC_LOG_TOFILE; + + if (versionsobj != NULL && cfg_obj_isuint32(versionsobj)) { + versions = cfg_obj_asuint32(versionsobj); + } else if (versionsobj != NULL && + cfg_obj_isstring(versionsobj) && + strcasecmp(cfg_obj_asstring(versionsobj), + "unlimited") == 0) + { + versions = ISC_LOG_ROLLINFINITE; + } + if (sizeobj != NULL && cfg_obj_isuint64(sizeobj) && + cfg_obj_asuint64(sizeobj) < maxoffset) + { + size = (isc_offset_t)cfg_obj_asuint64(sizeobj); + } + if (suffixobj != NULL && cfg_obj_isstring(suffixobj) && + strcasecmp(cfg_obj_asstring(suffixobj), "timestamp") == 0) + { + suffix = isc_log_rollsuffix_timestamp; + } + + dest.file.stream = NULL; + dest.file.name = cfg_obj_asstring(pathobj); + dest.file.versions = versions; + dest.file.suffix = suffix; + dest.file.maximum_size = size; + } else if (syslogobj != NULL) { + int facility = LOG_DAEMON; + + type = ISC_LOG_TOSYSLOG; + + if (cfg_obj_isstring(syslogobj)) { + const char *facilitystr = cfg_obj_asstring(syslogobj); + (void)isc_syslog_facilityfromstring(facilitystr, + &facility); + } + dest.facility = facility; + } else if (stderrobj != NULL) { + type = ISC_LOG_TOFILEDESC; + dest.file.stream = stderr; + dest.file.name = NULL; + dest.file.versions = ISC_LOG_ROLLNEVER; + dest.file.suffix = isc_log_rollsuffix_increment; + dest.file.maximum_size = 0; + } + + /* + * Munge flags. + */ + { + const cfg_obj_t *printcat = NULL; + const cfg_obj_t *printsev = NULL; + const cfg_obj_t *printtime = NULL; + const cfg_obj_t *buffered = NULL; + + (void)cfg_map_get(channel, "print-category", &printcat); + (void)cfg_map_get(channel, "print-severity", &printsev); + (void)cfg_map_get(channel, "print-time", &printtime); + (void)cfg_map_get(channel, "buffered", &buffered); + + if (printcat != NULL && cfg_obj_asboolean(printcat)) { + flags |= ISC_LOG_PRINTCATEGORY; + } + if (printsev != NULL && cfg_obj_asboolean(printsev)) { + flags |= ISC_LOG_PRINTLEVEL; + } + if (buffered != NULL && cfg_obj_asboolean(buffered)) { + flags |= ISC_LOG_BUFFERED; + } + if (printtime != NULL && cfg_obj_isboolean(printtime)) { + if (cfg_obj_asboolean(printtime)) { + flags |= ISC_LOG_PRINTTIME; + } + } else if (printtime != NULL) { /* local/iso8601/iso8601-utc */ + const char *s = cfg_obj_asstring(printtime); + flags |= ISC_LOG_PRINTTIME; + if (strcasecmp(s, "iso8601") == 0) { + flags |= ISC_LOG_ISO8601; + } else if (strcasecmp(s, "iso8601-utc") == 0) { + flags |= ISC_LOG_ISO8601 | ISC_LOG_UTC; + } + } + } + + level = ISC_LOG_INFO; + if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) { + if (cfg_obj_isstring(severity)) { + const char *str = cfg_obj_asstring(severity); + if (strcasecmp(str, "critical") == 0) { + level = ISC_LOG_CRITICAL; + } else if (strcasecmp(str, "error") == 0) { + level = ISC_LOG_ERROR; + } else if (strcasecmp(str, "warning") == 0) { + level = ISC_LOG_WARNING; + } else if (strcasecmp(str, "notice") == 0) { + level = ISC_LOG_NOTICE; + } else if (strcasecmp(str, "info") == 0) { + level = ISC_LOG_INFO; + } else if (strcasecmp(str, "dynamic") == 0) { + level = ISC_LOG_DYNAMIC; + } + } else { + /* debug */ + level = cfg_obj_asuint32(severity); + } + } + + if (logconfig != NULL) { + isc_log_createchannel(logconfig, channelname, type, level, + &dest, flags); + } + + if (type == ISC_LOG_TOFILE) { + FILE *fp; + + /* + * Test to make sure that file is a plain file. + * Fix defect #22771 + */ + result = isc_file_isplainfile(dest.file.name); + if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) { + /* + * Test that the file can be opened, since + * isc_log_open() can't effectively report + * failures when called in isc_log_doit(). + */ + result = isc_stdio_open(dest.file.name, "a", &fp); + if (result != ISC_R_SUCCESS) { + if (logconfig != NULL && !named_g_nosyslog) { + syslog(LOG_ERR, + "isc_stdio_open '%s' failed: " + "%s", + dest.file.name, + isc_result_totext(result)); + } + } else { + (void)isc_stdio_close(fp); + } + goto done; + } + if (logconfig != NULL && !named_g_nosyslog) { + syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s", + dest.file.name, isc_result_totext(result)); + } + } + +done: + return (result); +} + +isc_result_t +named_logconfig(isc_logconfig_t *logconfig, const cfg_obj_t *logstmt) { + isc_result_t result; + const cfg_obj_t *channels = NULL; + const cfg_obj_t *categories = NULL; + const cfg_listelt_t *element; + bool default_set = false; + bool unmatched_set = false; + const cfg_obj_t *catname; + + if (logconfig != NULL) { + named_log_setdefaultchannels(logconfig); + named_log_setdefaultsslkeylogfile(logconfig); + } + + (void)cfg_map_get(logstmt, "channel", &channels); + for (element = cfg_list_first(channels); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *channel = cfg_listelt_value(element); + CHECK(channel_fromconf(channel, logconfig)); + } + + (void)cfg_map_get(logstmt, "category", &categories); + for (element = cfg_list_first(categories); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *category = cfg_listelt_value(element); + CHECK(category_fromconf(category, logconfig)); + if (!default_set) { + catname = cfg_tuple_get(category, "name"); + if (strcmp(cfg_obj_asstring(catname), "default") == 0) { + default_set = true; + } + } + if (!unmatched_set) { + catname = cfg_tuple_get(category, "name"); + if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0) + { + unmatched_set = true; + } + } + } + + if (logconfig != NULL && !default_set) { + CHECK(named_log_setdefaultcategory(logconfig)); + } + + if (logconfig != NULL && !unmatched_set) { + CHECK(named_log_setunmatchedcategory(logconfig)); + } + + return (ISC_R_SUCCESS); + +cleanup: + return (result); +} diff --git a/bin/named/main.c b/bin/named/main.c new file mode 100644 index 0000000..154e17e --- /dev/null +++ b/bin/named/main.c @@ -0,0 +1,1663 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DNSTAP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_GPERFTOOLS_PROFILER +#include +#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */ + +#ifdef HAVE_JSON_C +#include +#endif /* HAVE_JSON_C */ + +#ifdef HAVE_GEOIP2 +#include +#endif /* ifdef HAVE_GEOIP2 */ + +/* + * Defining NAMED_MAIN provides storage declarations (rather than extern) + * for variables in named/globals.h. + */ +#define NAMED_MAIN 1 + +#include + +#include +#include +#include +#include +#include /* Explicit, though named/log.h includes it. */ +#include +#include +#include +#include +#ifdef HAVE_LIBSCF +#include +#endif /* ifdef HAVE_LIBSCF */ + +#include +#include +#ifdef HAVE_LIBXML2 +#include +#include +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_ZLIB +#include +#endif /* ifdef HAVE_ZLIB */ +#ifdef HAVE_LIBNGHTTP2 +#include +#endif +/* + * Include header files for database drivers here. + */ +/* #include "xxdb.h" */ + +/* + * The maximum number of stack frames to dump on assertion failure. + */ +#ifndef BACKTRACE_MAXFRAME +#define BACKTRACE_MAXFRAME 128 +#endif /* ifndef BACKTRACE_MAXFRAME */ + +extern unsigned int dns_zone_mkey_hour; +extern unsigned int dns_zone_mkey_day; +extern unsigned int dns_zone_mkey_month; + +static bool want_stats = false; +static char program_name[NAME_MAX] = "named"; +static char absolute_conffile[PATH_MAX]; +static char saved_command_line[4096] = { 0 }; +static char ellipsis[5] = { 0 }; +static char version[512]; +static int maxudp = 0; + +/* + * -T options: + */ +static bool dropedns = false; +static bool ednsformerr = false; +static bool ednsnotimp = false; +static bool ednsrefused = false; +static bool fixedlocal = false; +static bool noaa = false; +static bool noedns = false; +static bool nonearest = false; +static bool nosoa = false; +static bool notcp = false; +static bool sigvalinsecs = false; +static bool transferinsecs = false; +static bool transferslowly = false; +static bool transferstuck = false; + +/* + * -4 and -6 + */ +static bool disable6 = false; +static bool disable4 = false; + +void +named_main_earlywarning(const char *format, ...) { + va_list args; + + va_start(args, format); + if (named_g_lctx != NULL) { + isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_WARNING, format, + args); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); +} + +void +named_main_earlyfatal(const char *format, ...) { + va_list args; + + va_start(args, format); + if (named_g_lctx != NULL) { + isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, format, + args); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to early fatal error)"); + } else { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + va_end(args); + + exit(1); +} + +noreturn static void +assertion_failed(const char *file, int line, isc_assertiontype_t type, + const char *cond); + +static void +assertion_failed(const char *file, int line, isc_assertiontype_t type, + const char *cond) { + void *tracebuf[BACKTRACE_MAXFRAME]; + int nframes; + + /* + * Handle assertion failures. + */ + + if (named_g_lctx != NULL) { + /* + * Reset the assertion callback in case it is the log + * routines causing the assertion. + */ + isc_assertion_setcallback(NULL); + + nframes = isc_backtrace(tracebuf, BACKTRACE_MAXFRAME); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d: %s(%s) failed%s", file, line, + isc_assertion_typetotext(type), cond, + (nframes > 0) ? ", back trace" : ""); + if (nframes > 0) { + char **strs = isc_backtrace_symbols(tracebuf, nframes); + if (strs != NULL) { + for (int i = 0; i < nframes; i++) { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, + ISC_LOG_CRITICAL, "%s", + strs[i]); + } + } + } + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to assertion failure)"); + } else { + fprintf(stderr, "%s:%d: %s(%s) failed\n", file, line, + isc_assertion_typetotext(type), cond); + fflush(stderr); + } + + if (named_g_coreok) { + abort(); + } + exit(1); +} + +noreturn static void +library_fatal_error(const char *file, int line, const char *func, + const char *format, va_list args) ISC_FORMAT_PRINTF(3, 0); + +static void +library_fatal_error(const char *file, int line, const char *func, + const char *format, va_list args) { + /* + * Handle isc_error_fatal() calls from our libraries. + */ + + if (named_g_lctx != NULL) { + /* + * Reset the error callback in case it is the log + * routines causing the assertion. + */ + isc_error_setfatal(NULL); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "%s:%d:%s(): fatal error: ", file, line, func); + isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, format, + args); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_CRITICAL, + "exiting (due to fatal error in library)"); + } else { + fprintf(stderr, "%s:%d:%s(): fatal error: ", file, line, func); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } + + if (named_g_coreok) { + abort(); + } + exit(1); +} + +static void +library_unexpected_error(const char *file, int line, const char *func, + const char *format, va_list args) + ISC_FORMAT_PRINTF(3, 0); + +static void +library_unexpected_error(const char *file, int line, const char *func, + const char *format, va_list args) { + /* + * Handle isc_error_unexpected() calls from our libraries. + */ + + if (named_g_lctx != NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_ERROR, + "%s:%d:%s(): unexpected error: ", file, line, + func); + isc_log_vwrite(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_ERROR, format, + args); + } else { + fprintf(stderr, "%s:%d:%s(): fatal error: ", file, line, func); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); + } +} + +static void +usage(void) { + fprintf(stderr, "usage: named [-4|-6] [-c conffile] [-d debuglevel] " + "[-D comment] [-E engine]\n" + " [-f|-g] [-L logfile] [-n number_of_cpus] " + "[-p port] [-s]\n" + " [-S sockets] [-t chrootdir] [-u " + "username] [-U listeners]\n" + " [-X lockfile] [-m " + "{usage|trace|record|size|mctx}]\n" + " [-M fill|nofill]\n" + "usage: named [-v|-V|-C]\n"); +} + +static void +save_command_line(int argc, char *argv[]) { + int i; + char *dst = saved_command_line; + char *eob = saved_command_line + sizeof(saved_command_line) - 1; + char *rollback = dst; + + for (i = 1; i < argc && dst < eob; i++) { + char *src = argv[i]; + bool quoted = false; + + rollback = dst; + *dst++ = ' '; + + while (*src != '\0' && dst < eob) { + if (isalnum(*(unsigned char *)src) || *src == ',' || + *src == '-' || *src == '_' || *src == '.' || + *src == '/') + { + *dst++ = *src++; + } else if (isprint(*(unsigned char *)src)) { + if (dst + 2 >= eob) { + goto add_ellipsis; + } + *dst++ = '\\'; + *dst++ = *src++; + } else { + /* + * Control character found in the input, + * quote the whole arg and restart + */ + if (!quoted) { + dst = rollback; + src = argv[i]; + + if (dst + 3 >= eob) { + goto add_ellipsis; + } + + *dst++ = ' '; + *dst++ = '$'; + *dst++ = '\''; + + quoted = true; + continue; + } else { + char tmp[5]; + int c = snprintf(tmp, sizeof(tmp), + "\\%03o", *src++); + if (dst + c >= eob) { + goto add_ellipsis; + } + memmove(dst, tmp, c); + dst += c; + } + } + } + if (quoted) { + if (dst == eob) { + goto add_ellipsis; + } + *dst++ = '\''; + } + } + + if (dst < eob) { + return; + } +add_ellipsis: + dst = rollback; + *dst = '\0'; + strlcpy(ellipsis, " ...", sizeof(ellipsis)); +} + +static int +parse_int(char *arg, const char *desc) { + char *endp; + int tmp; + long int ltmp; + + ltmp = strtol(arg, &endp, 10); + tmp = (int)ltmp; + if (*endp != '\0') { + named_main_earlyfatal("%s '%s' must be numeric", desc, arg); + } + if (tmp < 0 || tmp != ltmp) { + named_main_earlyfatal("%s '%s' out of range", desc, arg); + } + return (tmp); +} + +static struct flag_def { + const char *name; + unsigned int value; + bool negate; +} mem_debug_flags[] = { { "none", 0, false }, + { "trace", ISC_MEM_DEBUGTRACE, false }, + { "record", ISC_MEM_DEBUGRECORD, false }, + { "usage", ISC_MEM_DEBUGUSAGE, false }, + { NULL, 0, false } }, + mem_context_flags[] = { { "fill", ISC_MEMFLAG_FILL, false }, + { "nofill", ISC_MEMFLAG_FILL, true }, + { NULL, 0, false } }; + +static void +set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) { + bool clear = false; + + for (;;) { + const struct flag_def *def; + const char *end = strchr(arg, ','); + int arglen; + if (end == NULL) { + end = arg + strlen(arg); + } + arglen = (int)(end - arg); + for (def = defs; def->name != NULL; def++) { + if (arglen == (int)strlen(def->name) && + memcmp(arg, def->name, arglen) == 0) + { + if (def->value == 0) { + clear = true; + } + if (def->negate) { + *ret &= ~(def->value); + } else { + *ret |= def->value; + } + goto found; + } + } + named_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg); + found: + if (clear || (*end == '\0')) { + break; + } + arg = end + 1; + } + + if (clear) { + *ret = 0; + } +} + +static void +list_dnssec_algorithms(isc_buffer_t *b) { + for (dst_algorithm_t i = DST_ALG_UNKNOWN; i < DST_MAX_ALGS; i++) { + if (i == DST_ALG_DH || i == DST_ALG_GSSAPI || + (i >= DST_ALG_HMAC_FIRST && i <= DST_ALG_HMAC_LAST)) + { + continue; + } + if (dst_algorithm_supported(i)) { + isc_buffer_putstr(b, " "); + (void)dns_secalg_totext(i, b); + } + } +} + +static void +list_ds_algorithms(isc_buffer_t *b) { + for (size_t i = 0; i < 256; i++) { + if (dst_ds_digest_supported(i)) { + isc_buffer_putstr(b, " "); + (void)dns_dsdigest_totext(i, b); + } + } +} + +static void +list_hmac_algorithms(isc_buffer_t *b) { + isc_buffer_t sb = *b; + for (dst_algorithm_t i = DST_ALG_HMAC_FIRST; i <= DST_ALG_HMAC_LAST; + i++) + { + if (i == DST_ALG_GSSAPI) { + continue; + } + if (dst_algorithm_supported(i)) { + isc_buffer_putstr(b, " "); + isc_buffer_putstr(b, dst_hmac_algorithm_totext(i)); + } + } + for (unsigned char *s = isc_buffer_used(&sb); s != isc_buffer_used(b); + s++) + { + *s = toupper(*s); + } +} + +static void +logit(isc_buffer_t *b) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "%.*s", + (int)isc_buffer_usedlength(b), + (char *)isc_buffer_base(b)); +} + +static void +printit(isc_buffer_t *b) { + printf("%.*s\n", (int)isc_buffer_usedlength(b), + (char *)isc_buffer_base(b)); +} + +static void +format_supported_algorithms(void (*emit)(isc_buffer_t *b)) { + isc_buffer_t b; + char buf[512]; + + isc_buffer_init(&b, buf, sizeof(buf)); + isc_buffer_putstr(&b, "DNSSEC algorithms:"); + list_dnssec_algorithms(&b); + (*emit)(&b); + + isc_buffer_init(&b, buf, sizeof(buf)); + isc_buffer_putstr(&b, "DS algorithms:"); + list_ds_algorithms(&b); + (*emit)(&b); + + isc_buffer_init(&b, buf, sizeof(buf)); + isc_buffer_putstr(&b, "HMAC algorithms:"); + list_hmac_algorithms(&b); + (*emit)(&b); + + isc_buffer_init(&b, buf, sizeof(buf)); + isc_buffer_printf(&b, "TKEY mode 2 support (Diffie-Hellman): %s", + (dst_algorithm_supported(DST_ALG_DH) && + dst_algorithm_supported(DST_ALG_HMACMD5)) + ? "yes" + : "non"); + (*emit)(&b); + + isc_buffer_init(&b, buf, sizeof(buf)); + isc_buffer_printf(&b, "TKEY mode 3 support (GSS-API): %s", + dst_algorithm_supported(DST_ALG_GSSAPI) ? "yes" + : "no"); + (*emit)(&b); +} + +static void +printversion(bool verbose) { + char rndcconf[PATH_MAX], *dot = NULL; + isc_mem_t *mctx = NULL; + isc_result_t result; + isc_buffer_t b; + char buf[512]; +#if defined(HAVE_GEOIP2) + cfg_parser_t *parser = NULL; + cfg_obj_t *config = NULL; + const cfg_obj_t *defaults = NULL, *obj = NULL; +#endif /* if defined(HAVE_GEOIP2) */ + + printf("%s%s \n", PACKAGE_STRING, PACKAGE_DESCRIPTION, + PACKAGE_SRCID); + + if (!verbose) { + return; + } + + printf("running on %s\n", named_os_uname()); + printf("built by %s with %s\n", PACKAGE_BUILDER, PACKAGE_CONFIGARGS); +#ifdef __clang__ + printf("compiled by CLANG %s\n", __VERSION__); +#else /* ifdef __clang__ */ +#if defined(__ICC) || defined(__INTEL_COMPILER) + printf("compiled by ICC %s\n", __VERSION__); +#else /* if defined(__ICC) || defined(__INTEL_COMPILER) */ +#ifdef __GNUC__ + printf("compiled by GCC %s\n", __VERSION__); +#endif /* ifdef __GNUC__ */ +#endif /* if defined(__ICC) || defined(__INTEL_COMPILER) */ +#endif /* ifdef __clang__ */ +#ifdef _MSC_VER + printf("compiled by MSVC %d\n", _MSC_VER); +#endif /* ifdef _MSC_VER */ +#ifdef __SUNPRO_C + printf("compiled by Solaris Studio %x\n", __SUNPRO_C); +#endif /* ifdef __SUNPRO_C */ + printf("compiled with OpenSSL version: %s\n", OPENSSL_VERSION_TEXT); +#if !defined(LIBRESSL_VERSION_NUMBER) && \ + OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */ + printf("linked to OpenSSL version: %s\n", + OpenSSL_version(OPENSSL_VERSION)); + +#else /* if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= \ + * 0x10100000L */ + printf("linked to OpenSSL version: %s\n", + SSLeay_version(SSLEAY_VERSION)); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + printf("compiled with libuv version: %d.%d.%d\n", UV_VERSION_MAJOR, + UV_VERSION_MINOR, UV_VERSION_PATCH); + printf("linked to libuv version: %s\n", uv_version_string()); +#if HAVE_LIBNGHTTP2 + nghttp2_info *nginfo = NULL; + printf("compiled with libnghttp2 version: %s\n", NGHTTP2_VERSION); + nginfo = nghttp2_version(1); + printf("linked to libnghttp2 version: %s\n", nginfo->version_str); +#endif +#ifdef HAVE_LIBXML2 + printf("compiled with libxml2 version: %s\n", LIBXML_DOTTED_VERSION); + printf("linked to libxml2 version: %s\n", xmlParserVersion); +#endif /* ifdef HAVE_LIBXML2 */ +#if defined(HAVE_JSON_C) + printf("compiled with json-c version: %s\n", JSON_C_VERSION); + printf("linked to json-c version: %s\n", json_c_version()); +#endif /* if defined(HAVE_JSON_C) */ +#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) + printf("compiled with zlib version: %s\n", ZLIB_VERSION); + printf("linked to zlib version: %s\n", zlibVersion()); +#endif /* if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) */ +#if defined(HAVE_GEOIP2) + /* Unfortunately, no version define on link time */ + printf("linked to maxminddb version: %s\n", MMDB_lib_version()); +#endif /* if defined(HAVE_GEOIP2) */ +#if defined(HAVE_DNSTAP) + printf("compiled with protobuf-c version: %s\n", PROTOBUF_C_VERSION); + printf("linked to protobuf-c version: %s\n", protobuf_c_version()); +#endif /* if defined(HAVE_DNSTAP) */ + printf("threads support is enabled\n"); + + isc_mem_create(&mctx); + result = dst_lib_init(mctx, named_g_engine); + if (result == ISC_R_SUCCESS) { + isc_buffer_init(&b, buf, sizeof(buf)); + format_supported_algorithms(printit); + printf("\n"); + dst_lib_destroy(); + } else { + printf("DST initialization failure: %s\n", + isc_result_totext(result)); + } + + /* + * The default rndc.conf and rndc.key paths are in the same + * directory, but named only has rndc.key defined internally. + * We construct the rndc.conf path from it. + */ + strlcpy(rndcconf, named_g_keyfile, sizeof(rndcconf)); + dot = strrchr(rndcconf, '.'); + if (dot != NULL) { + size_t len = dot - rndcconf + 1; + snprintf(dot + 1, PATH_MAX - len, "conf"); + } + + /* + * Print default configuration paths. + */ + printf("default paths:\n"); + printf(" named configuration: %s\n", named_g_conffile); + printf(" rndc configuration: %s\n", rndcconf); + printf(" DNSSEC root key: %s\n", named_g_defaultbindkeys); + printf(" nsupdate session key: %s\n", named_g_defaultsessionkeyfile); + printf(" named PID file: %s\n", named_g_defaultpidfile); + printf(" named lock file: %s\n", named_g_defaultlockfile); +#if defined(HAVE_GEOIP2) +#define RTC(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS) + RTC(cfg_parser_create(mctx, named_g_lctx, &parser)); + RTC(named_config_parsedefaults(parser, &config)); + RTC(cfg_map_get(config, "options", &defaults)); + RTC(cfg_map_get(defaults, "geoip-directory", &obj)); + if (cfg_obj_isstring(obj)) { + printf(" geoip-directory: %s\n", cfg_obj_asstring(obj)); + } + cfg_obj_destroy(parser, &config); + cfg_parser_destroy(&parser); + isc_mem_detach(&mctx); +#endif /* HAVE_GEOIP2 */ +} + +static void +parse_fuzz_arg(void) { + if (!strncmp(isc_commandline_argument, "client:", 7)) { + named_g_fuzz_addr = isc_commandline_argument + 7; + named_g_fuzz_type = isc_fuzz_client; + } else if (!strncmp(isc_commandline_argument, "tcp:", 4)) { + named_g_fuzz_addr = isc_commandline_argument + 4; + named_g_fuzz_type = isc_fuzz_tcpclient; + } else if (!strncmp(isc_commandline_argument, "resolver:", 9)) { + named_g_fuzz_addr = isc_commandline_argument + 9; + named_g_fuzz_type = isc_fuzz_resolver; + } else if (!strncmp(isc_commandline_argument, "http:", 5)) { + named_g_fuzz_addr = isc_commandline_argument + 5; + named_g_fuzz_type = isc_fuzz_http; + } else if (!strncmp(isc_commandline_argument, "rndc:", 5)) { + named_g_fuzz_addr = isc_commandline_argument + 5; + named_g_fuzz_type = isc_fuzz_rndc; + } else { + named_main_earlyfatal("unknown fuzzing type '%s'", + isc_commandline_argument); + } +} + +static void +parse_T_opt(char *option) { + const char *p; + char *last = NULL; + /* + * force the server to behave (or misbehave) in + * specified ways for testing purposes. + */ + if (!strcmp(option, "dropedns")) { + dropedns = true; + } else if (!strcmp(option, "ednsformerr")) { + ednsformerr = true; + } else if (!strcmp(option, "ednsnotimp")) { + ednsnotimp = true; + } else if (!strcmp(option, "ednsrefused")) { + ednsrefused = true; + } else if (!strcmp(option, "fixedlocal")) { + fixedlocal = true; + } else if (!strcmp(option, "keepstderr")) { + named_g_keepstderr = true; + } else if (!strcmp(option, "noaa")) { + noaa = true; + } else if (!strcmp(option, "noedns")) { + noedns = true; + } else if (!strcmp(option, "nonearest")) { + nonearest = true; + } else if (!strcmp(option, "nosoa")) { + nosoa = true; + } else if (!strcmp(option, "nosyslog")) { + named_g_nosyslog = true; + } else if (!strcmp(option, "notcp")) { + notcp = true; + } else if (!strncmp(option, "maxcachesize=", 13)) { + named_g_maxcachesize = atoi(option + 13); + } else if (!strcmp(option, "maxudp512")) { + maxudp = 512; + } else if (!strcmp(option, "maxudp1460")) { + maxudp = 1460; + } else if (!strncmp(option, "maxudp=", 7)) { + maxudp = atoi(option + 7); + if (maxudp <= 0) { + named_main_earlyfatal("bad maxudp"); + } + } else if (!strncmp(option, "mkeytimers=", 11)) { + p = strtok_r(option + 11, "/", &last); + if (p == NULL) { + named_main_earlyfatal("bad mkeytimer"); + } + + dns_zone_mkey_hour = atoi(p); + if (dns_zone_mkey_hour == 0) { + named_main_earlyfatal("bad mkeytimer"); + } + + p = strtok_r(NULL, "/", &last); + if (p == NULL) { + dns_zone_mkey_day = (24 * dns_zone_mkey_hour); + dns_zone_mkey_month = (30 * dns_zone_mkey_day); + return; + } + + dns_zone_mkey_day = atoi(p); + if (dns_zone_mkey_day < dns_zone_mkey_hour) { + named_main_earlyfatal("bad mkeytimer"); + } + + p = strtok_r(NULL, "/", &last); + if (p == NULL) { + dns_zone_mkey_month = (30 * dns_zone_mkey_day); + return; + } + + dns_zone_mkey_month = atoi(p); + if (dns_zone_mkey_month < dns_zone_mkey_day) { + named_main_earlyfatal("bad mkeytimer"); + } + } else if (!strcmp(option, "sigvalinsecs")) { + sigvalinsecs = true; + } else if (!strcmp(option, "transferinsecs")) { + transferinsecs = true; + } else if (!strcmp(option, "transferslowly")) { + transferslowly = true; + } else if (!strcmp(option, "transferstuck")) { + transferstuck = true; + } else if (!strncmp(option, "tat=", 4)) { + named_g_tat_interval = atoi(option + 4); + } else { + fprintf(stderr, "unknown -T flag '%s'\n", option); + } +} + +static void +parse_port(char *arg) { + enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT; + char *value = arg; + int port; + + if (strncmp(arg, "dns=", 4) == 0) { + value = arg + 4; + } else if (strncmp(arg, "tls=", 4) == 0) { + value = arg + 4; + ptype = TLSPORT; + } else if (strncmp(arg, "https=", 6) == 0) { + value = arg + 6; + ptype = HTTPSPORT; + } else if (strncmp(arg, "http=", 5) == 0) { + value = arg + 6; + ptype = HTTPPORT; + } + + port = parse_int(value, "port"); + if (port < 1 || port > 65535) { + named_main_earlyfatal("port '%s' out of range", value); + } + + switch (ptype) { + case DNSPORT: + named_g_port = port; + break; + case TLSPORT: + named_g_tlsport = port; + break; + case HTTPSPORT: + named_g_httpsport = port; + break; + case HTTPPORT: + named_g_httpport = port; + break; + default: + UNREACHABLE(); + } +} + +static void +parse_command_line(int argc, char *argv[]) { + int ch; + const char *p; + + save_command_line(argc, argv); + + /* + * NAMED_MAIN_ARGS is defined in main.h, so that it can be used + * both by named and by ntservice hooks. + */ + isc_commandline_errprint = false; + while ((ch = isc_commandline_parse(argc, argv, NAMED_MAIN_ARGS)) != -1) + { + switch (ch) { + case '4': + if (disable4) { + named_main_earlyfatal("cannot specify " + "-4 and -6"); + } + if (isc_net_probeipv4() != ISC_R_SUCCESS) { + named_main_earlyfatal("IPv4 not supported " + "by OS"); + } + isc_net_disableipv6(); + disable6 = true; + break; + case '6': + if (disable6) { + named_main_earlyfatal("cannot specify " + "-4 and -6"); + } + if (isc_net_probeipv6() != ISC_R_SUCCESS) { + named_main_earlyfatal("IPv6 not supported " + "by OS"); + } + isc_net_disableipv4(); + disable4 = true; + break; + case 'A': + parse_fuzz_arg(); + break; + case 'c': + named_g_conffile = isc_commandline_argument; + named_g_conffileset = true; + break; + case 'C': + printf("# Built-in default values. " + "This is NOT the run-time configuration!\n"); + printf("%s", named_config_getdefault()); + exit(0); + case 'd': + named_g_debuglevel = parse_int(isc_commandline_argument, + "debug " + "level"); + break; + case 'D': + /* Descriptive comment for 'ps'. */ + break; + case 'E': + named_g_engine = isc_commandline_argument; + break; + case 'f': + named_g_foreground = true; + break; + case 'g': + named_g_foreground = true; + named_g_logstderr = true; + break; + case 'L': + named_g_logfile = isc_commandline_argument; + break; + case 'M': + set_flags(isc_commandline_argument, mem_context_flags, + &isc_mem_defaultflags); + break; + case 'm': + set_flags(isc_commandline_argument, mem_debug_flags, + &isc_mem_debugging); + break; + case 'N': /* Deprecated. */ + case 'n': + named_g_cpus = parse_int(isc_commandline_argument, + "number of cpus"); + if (named_g_cpus == 0) { + named_g_cpus = 1; + } + break; + case 'p': + parse_port(isc_commandline_argument); + break; + case 's': + /* XXXRTH temporary syntax */ + want_stats = true; + break; + case 'S': + /* Formerly maxsocks */ + break; + case 't': + /* XXXJAB should we make a copy? */ + named_g_chrootdir = isc_commandline_argument; + break; + case 'T': /* NOT DOCUMENTED */ + parse_T_opt(isc_commandline_argument); + break; + case 'U': + named_g_udpdisp = parse_int(isc_commandline_argument, + "number of UDP listeners " + "per interface"); + break; + case 'u': + named_g_username = isc_commandline_argument; + break; + case 'v': + printversion(false); + exit(0); + case 'V': + printversion(true); + exit(0); + case 'x': + /* Obsolete. No longer in use. Ignore. */ + break; + case 'X': + named_g_forcelock = true; + if (strcasecmp(isc_commandline_argument, "none") != 0) { + named_g_defaultlockfile = + isc_commandline_argument; + } else { + named_g_defaultlockfile = NULL; + } + break; + case 'F': + /* Reserved for FIPS mode */ + FALLTHROUGH; + case '?': + usage(); + if (isc_commandline_option == '?') { + exit(0); + } + p = strchr(NAMED_MAIN_ARGS, isc_commandline_option); + if (p == NULL || *++p != ':') { + named_main_earlyfatal("unknown option '-%c'", + isc_commandline_option); + } else { + named_main_earlyfatal("option '-%c' requires " + "an argument", + isc_commandline_option); + } + FALLTHROUGH; + default: + named_main_earlyfatal("parsing options returned %d", + ch); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + POST(argv); + + if (argc > 0) { + usage(); + named_main_earlyfatal("extra command line arguments"); + } +} + +static isc_result_t +create_managers(void) { + isc_result_t result; + + INSIST(named_g_cpus_detected > 0); + + if (named_g_cpus == 0) { + named_g_cpus = named_g_cpus_detected; + } + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s", + named_g_cpus_detected, named_g_cpus_detected == 1 ? "" : "s", + named_g_cpus, named_g_cpus == 1 ? "" : "s"); + if (named_g_udpdisp == 0) { + named_g_udpdisp = named_g_cpus_detected; + } + if (named_g_udpdisp > named_g_cpus) { + named_g_udpdisp = named_g_cpus; + } + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "using %u UDP listener%s per interface", named_g_udpdisp, + named_g_udpdisp == 1 ? "" : "s"); + + result = isc_managers_create(named_g_mctx, named_g_cpus, + 0 /* quantum */, &named_g_netmgr, + &named_g_taskmgr, &named_g_timermgr); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_nm_maxudp(named_g_netmgr, maxudp); + + return (ISC_R_SUCCESS); +} + +static void +destroy_managers(void) { + isc_managers_destroy(&named_g_netmgr, &named_g_taskmgr, + &named_g_timermgr); +} + +static void +setup(void) { + isc_result_t result; + isc_resourcevalue_t old_openfiles; + ns_server_t *sctx; +#ifdef HAVE_LIBSCF + char *instance = NULL; +#endif /* ifdef HAVE_LIBSCF */ + + /* + * Get the user and group information before changing the root + * directory, so the administrator does not need to keep a copy + * of the user and group databases in the chroot'ed environment. + */ + named_os_inituserinfo(named_g_username); + + /* + * Initialize time conversion information + */ + named_os_tzset(); + + named_os_opendevnull(); + +#ifdef HAVE_LIBSCF + /* Check if named is under smf control, before chroot. */ + result = named_smf_get_instance(&instance, 0, named_g_mctx); + /* We don't care about instance, just check if we got one. */ + if (result == ISC_R_SUCCESS) { + named_smf_got_instance = 1; + } else { + named_smf_got_instance = 0; + } + if (instance != NULL) { + isc_mem_free(named_g_mctx, instance); + } +#endif /* HAVE_LIBSCF */ + + /* + * Check for the number of cpu's before named_os_chroot(). + */ + named_g_cpus_detected = isc_os_ncpus(); + + named_os_chroot(named_g_chrootdir); + + /* + * For operating systems which have a capability mechanism, now + * is the time to switch to minimal privs and change our user id. + * On traditional UNIX systems, this call will be a no-op, and we + * will change the user ID after reading the config file the first + * time. (We need to read the config file to know which possibly + * privileged ports to bind() to.) + */ + named_os_minprivs(); + + result = named_log_init(named_g_username != NULL); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("named_log_init() failed: %s", + isc_result_totext(result)); + } + + /* + * Now is the time to daemonize (if we're not running in the + * foreground). We waited until now because we wanted to get + * a valid logging context setup. We cannot daemonize any later, + * because calling create_managers() will create threads, which + * would be lost after fork(). + */ + if (!named_g_foreground) { + named_os_daemonize(); + } + + /* + * We call isc_app_start() here as some versions of FreeBSD's fork() + * destroys all the signal handling it sets up. + */ + result = isc_app_start(); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("isc_app_start() failed: %s", + isc_result_totext(result)); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "starting %s%s ", PACKAGE_STRING, + PACKAGE_DESCRIPTION, PACKAGE_SRCID); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "running on %s", + named_os_uname()); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "built with %s", + PACKAGE_CONFIGARGS); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "running as: %s%s%s", program_name, saved_command_line, + ellipsis); +#ifdef __clang__ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled by CLANG %s", __VERSION__); +#else /* ifdef __clang__ */ +#if defined(__ICC) || defined(__INTEL_COMPILER) + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled by ICC %s", __VERSION__); +#else /* if defined(__ICC) || defined(__INTEL_COMPILER) */ +#ifdef __GNUC__ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled by GCC %s", __VERSION__); +#endif /* ifdef __GNUC__ */ +#endif /* if defined(__ICC) || defined(__INTEL_COMPILER) */ +#endif /* ifdef __clang__ */ +#ifdef _MSC_VER + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled by MSVC %d", _MSC_VER); +#endif /* ifdef _MSC_VER */ +#ifdef __SUNPRO_C + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled by Solaris Studio %x", __SUNPRO_C); +#endif /* ifdef __SUNPRO_C */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled with OpenSSL version: %s", + OPENSSL_VERSION_TEXT); +#if !defined(LIBRESSL_VERSION_NUMBER) && \ + OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to OpenSSL version: %s", + OpenSSL_version(OPENSSL_VERSION)); +#else /* if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= \ + * 0x10100000L */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to OpenSSL version: %s", + SSLeay_version(SSLEAY_VERSION)); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled with libuv version: %d.%d.%d", UV_VERSION_MAJOR, + UV_VERSION_MINOR, UV_VERSION_PATCH); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to libuv version: %s", uv_version_string()); +#ifdef HAVE_LIBXML2 + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled with libxml2 version: %s", + LIBXML_DOTTED_VERSION); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to libxml2 version: %s", xmlParserVersion); +#endif /* ifdef HAVE_LIBXML2 */ +#if defined(HAVE_JSON_C) + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled with json-c version: %s", JSON_C_VERSION); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to json-c version: %s", json_c_version()); +#endif /* if defined(HAVE_JSON_C) */ +#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "compiled with zlib version: %s", ZLIB_VERSION); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "linked to zlib version: %s", zlibVersion()); +#endif /* if defined(HAVE_ZLIB) && defined(ZLIB_VERSION) */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "----------------------------------------------------"); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "BIND 9 is maintained by Internet Systems Consortium,"); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "Inc. (ISC), a non-profit 501(c)(3) public-benefit "); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "corporation. Support and training for BIND 9 are "); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "available at https://www.isc.org/support"); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "----------------------------------------------------"); + + /* + * Get the initial resource limits. + */ + RUNTIME_CHECK(isc_resource_getlimit(isc_resource_stacksize, + &named_g_initstacksize) == + ISC_R_SUCCESS); + RUNTIME_CHECK(isc_resource_getlimit(isc_resource_datasize, + &named_g_initdatasize) == + ISC_R_SUCCESS); + RUNTIME_CHECK(isc_resource_getlimit(isc_resource_coresize, + &named_g_initcoresize) == + ISC_R_SUCCESS); + RUNTIME_CHECK(isc_resource_getlimit(isc_resource_openfiles, + &named_g_initopenfiles) == + ISC_R_SUCCESS); + + /* + * System resources cannot effectively be tuned on some systems. + * Raise the limit in such cases for safety. + */ + old_openfiles = named_g_initopenfiles; + named_os_adjustnofile(); + RUNTIME_CHECK(isc_resource_getlimit(isc_resource_openfiles, + &named_g_initopenfiles) == + ISC_R_SUCCESS); + if (old_openfiles != named_g_initopenfiles) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, + "adjusted limit on open files from " + "%" PRIu64 " to " + "%" PRIu64, + old_openfiles, named_g_initopenfiles); + } + + /* + * If the named configuration filename is relative, prepend the current + * directory's name before possibly changing to another directory. + */ + if (!isc_file_isabsolute(named_g_conffile)) { + result = isc_file_absolutepath(named_g_conffile, + absolute_conffile, + sizeof(absolute_conffile)); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("could not construct " + "absolute path " + "of configuration file: %s", + isc_result_totext(result)); + } + named_g_conffile = absolute_conffile; + } + + /* + * Record the server's startup time. + */ + result = isc_time_now(&named_g_boottime); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("isc_time_now() failed: %s", + isc_result_totext(result)); + } + + result = create_managers(); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("create_managers() failed: %s", + isc_result_totext(result)); + } + + named_builtin_init(); + + /* + * Add calls to register sdb drivers here. + */ + /* xxdb_init(); */ + + /* + * Register the DLZ "dlopen" driver. + */ + result = dlz_dlopen_init(named_g_mctx); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("dlz_dlopen_init() failed: %s", + isc_result_totext(result)); + } + + named_server_create(named_g_mctx, &named_g_server); + ENSURE(named_g_server != NULL); + sctx = named_g_server->sctx; + + /* + * Report supported algorithms now that dst_lib_init() has + * been called via named_server_create(). + */ + format_supported_algorithms(logit); + + /* + * Modify server context according to command line options + */ + if (disable4) { + ns_server_setoption(sctx, NS_SERVER_DISABLE4, true); + } + if (disable6) { + ns_server_setoption(sctx, NS_SERVER_DISABLE6, true); + } + if (dropedns) { + ns_server_setoption(sctx, NS_SERVER_DROPEDNS, true); + } + if (ednsformerr) { /* STD13 server */ + ns_server_setoption(sctx, NS_SERVER_EDNSFORMERR, true); + } + if (ednsnotimp) { + ns_server_setoption(sctx, NS_SERVER_EDNSNOTIMP, true); + } + if (ednsrefused) { + ns_server_setoption(sctx, NS_SERVER_EDNSREFUSED, true); + } + if (fixedlocal) { + ns_server_setoption(sctx, NS_SERVER_FIXEDLOCAL, true); + } + if (noaa) { + ns_server_setoption(sctx, NS_SERVER_NOAA, true); + } + if (noedns) { + ns_server_setoption(sctx, NS_SERVER_NOEDNS, true); + } + if (nonearest) { + ns_server_setoption(sctx, NS_SERVER_NONEAREST, true); + } + if (nosoa) { + ns_server_setoption(sctx, NS_SERVER_NOSOA, true); + } + if (notcp) { + ns_server_setoption(sctx, NS_SERVER_NOTCP, true); + } + if (sigvalinsecs) { + ns_server_setoption(sctx, NS_SERVER_SIGVALINSECS, true); + } + if (transferinsecs) { + ns_server_setoption(sctx, NS_SERVER_TRANSFERINSECS, true); + } + if (transferslowly) { + ns_server_setoption(sctx, NS_SERVER_TRANSFERSLOWLY, true); + } + if (transferstuck) { + ns_server_setoption(sctx, NS_SERVER_TRANSFERSTUCK, true); + } +} + +static void +cleanup(void) { + destroy_managers(); + + if (named_g_mapped != NULL) { + dns_acl_detach(&named_g_mapped); + } + + named_server_destroy(&named_g_server); + + named_builtin_deinit(); + + /* + * Add calls to unregister sdb drivers here. + */ + /* xxdb_clear(); */ + + /* + * Unregister "dlopen" DLZ driver. + */ + dlz_dlopen_clear(); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_MAIN, ISC_LOG_NOTICE, "exiting"); + named_log_shutdown(); +} + +static char *memstats = NULL; + +void +named_main_setmemstats(const char *filename) { + /* + * Caller has to ensure locking. + */ + + if (memstats != NULL) { + free(memstats); + memstats = NULL; + } + + if (filename == NULL) { + return; + } + + memstats = strdup(filename); +} + +#ifdef HAVE_LIBSCF +/* + * Get FMRI for the named process. + */ +isc_result_t +named_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) { + scf_handle_t *h = NULL; + int namelen; + char *instance; + + REQUIRE(ins_name != NULL && *ins_name == NULL); + + if ((h = scf_handle_create(SCF_VERSION)) == NULL) { + if (debug) { + UNEXPECTED_ERROR("scf_handle_create() failed: %s", + scf_strerror(scf_error())); + } + return (ISC_R_FAILURE); + } + + if (scf_handle_bind(h) == -1) { + if (debug) { + UNEXPECTED_ERROR("scf_handle_bind() failed: %s", + scf_strerror(scf_error())); + } + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if ((namelen = scf_myname(h, NULL, 0)) == -1) { + if (debug) { + UNEXPECTED_ERROR("scf_myname() failed: %s", + scf_strerror(scf_error())); + } + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) { + UNEXPECTED_ERROR("named_smf_get_instance memory " + "allocation failed: %s", + isc_result_totext(ISC_R_NOMEMORY)); + scf_handle_destroy(h); + return (ISC_R_FAILURE); + } + + if (scf_myname(h, instance, namelen + 1) == -1) { + if (debug) { + UNEXPECTED_ERROR("scf_myname() failed: %s", + scf_strerror(scf_error())); + } + scf_handle_destroy(h); + isc_mem_free(mctx, instance); + return (ISC_R_FAILURE); + } + + scf_handle_destroy(h); + *ins_name = instance; + return (ISC_R_SUCCESS); +} +#endif /* HAVE_LIBSCF */ + +/* main entry point, possibly hooked */ + +int +main(int argc, char *argv[]) { + isc_result_t result; +#ifdef HAVE_LIBSCF + char *instance = NULL; +#endif /* ifdef HAVE_LIBSCF */ + +#ifdef HAVE_GPERFTOOLS_PROFILER + (void)ProfilerStart(NULL); +#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */ + +#ifdef HAVE_LIBXML2 + xmlInitParser(); +#endif /* HAVE_LIBXML2 */ + + /* + * Technically, this call is superfluous because on startup of the main + * program, the portable "C" locale is selected by default. This + * explicit call here is for a reference that the BIND 9 code base is + * not locale aware and the locale MUST be set to "C" (or "POSIX") when + * calling any BIND 9 library code. If you are calling external + * libraries that use locale, such calls must be wrapped into + * setlocale(LC_ALL, ""); before the call and setlocale(LC_ALL, "C"); + * after the call, and no BIND 9 library calls must be made in between. + */ + setlocale(LC_ALL, "C"); + + /* + * Record version in core image. + * strings named.core | grep "named version:" + */ + strlcat(version, +#if defined(NO_VERSION_DATE) || !defined(__DATE__) + "named version: BIND " PACKAGE_VERSION " <" PACKAGE_SRCID ">", +#else + "named version: BIND " PACKAGE_VERSION " <" PACKAGE_SRCID + "> (" __DATE__ ")", +#endif + sizeof(version)); + result = isc_file_progname(*argv, program_name, sizeof(program_name)); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("program name too long"); + } + + isc_assertion_setcallback(assertion_failed); + isc_error_setfatal(library_fatal_error); + isc_error_setunexpected(library_unexpected_error); + + named_os_init(program_name); + + parse_command_line(argc, argv); + +#ifdef ENABLE_AFL + if (named_g_fuzz_type != isc_fuzz_none) { + named_fuzz_setup(); + } + + if (named_g_fuzz_type == isc_fuzz_resolver) { + dns_resolver_setfuzzing(); + } else if (named_g_fuzz_type == isc_fuzz_http) { + isc_httpd_setfinishhook(named_fuzz_notify); + } +#endif /* ifdef ENABLE_AFL */ + /* + * Warn about common configuration error. + */ + if (named_g_chrootdir != NULL) { + int len = strlen(named_g_chrootdir); + if (strncmp(named_g_chrootdir, named_g_conffile, len) == 0 && + (named_g_conffile[len] == '/' || + named_g_conffile[len] == '\\')) + { + named_main_earlywarning("config filename (-c %s) " + "contains chroot path (-t %s)", + named_g_conffile, + named_g_chrootdir); + } + } + + isc_mem_create(&named_g_mctx); + isc_mem_setname(named_g_mctx, "main"); + + setup(); + INSIST(named_g_server != NULL); + + /* + * Start things running and then wait for a shutdown request + * or reload. + */ + do { + result = isc_app_run(); + + if (result == ISC_R_RELOAD) { + named_server_reloadwanted(named_g_server); + } else if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR("isc_app_run(): %s", + isc_result_totext(result)); + /* + * Force exit. + */ + result = ISC_R_SUCCESS; + } + } while (result != ISC_R_SUCCESS); + +#ifdef HAVE_LIBSCF + if (named_smf_want_disable == 1) { + result = named_smf_get_instance(&instance, 1, named_g_mctx); + if (result == ISC_R_SUCCESS && instance != NULL) { + if (smf_disable_instance(instance, 0) != 0) { + UNEXPECTED_ERROR("smf_disable_instance() " + "failed for %s : %s", + instance, + scf_strerror(scf_error())); + } + } + if (instance != NULL) { + isc_mem_free(named_g_mctx, instance); + } + } +#endif /* HAVE_LIBSCF */ + + cleanup(); + + if (want_stats) { + isc_mem_stats(named_g_mctx, stdout); + } + + if (named_g_memstatistics && memstats != NULL) { + FILE *fp = NULL; + result = isc_stdio_open(memstats, "w", &fp); + if (result == ISC_R_SUCCESS) { + isc_mem_stats(named_g_mctx, fp); + (void)isc_stdio_close(fp); + } + } + isc_mem_destroy(&named_g_mctx); + isc_mem_checkdestroyed(stderr); + + named_main_setmemstats(NULL); + + isc_app_finish(); + + named_os_closedevnull(); + + named_os_shutdown(); + +#ifdef HAVE_LIBXML2 + xmlCleanupParser(); +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_GPERFTOOLS_PROFILER + ProfilerStop(); +#endif /* ifdef HAVE_GPERFTOOLS_PROFILER */ + + return (0); +} diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst new file mode 100644 index 0000000..8e93f8b --- /dev/null +++ b/bin/named/named.conf.rst @@ -0,0 +1,67 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: named.conf + +named.conf - configuration file for **named** +--------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`named.conf` + +Description +~~~~~~~~~~~ + +:file:`named.conf` is the configuration file for :iscman:`named`. + +For complete documentation about the configuration statements, please refer to +the Configuration Reference section in the BIND 9 Administrator Reference +Manual. + +Statements are enclosed in braces and terminated with a semi-colon. +Clauses in the statements are also semi-colon terminated. The usual +comment styles are supported: + +C style: /\* \*/ + +C++ style: // to end of line + +Unix style: # to end of line + +.. literalinclude:: ../../doc/misc/options + +Any of these zone statements can also be set inside the view statement. + +.. literalinclude:: ../../doc/misc/primary.zoneopt +.. literalinclude:: ../../doc/misc/secondary.zoneopt +.. literalinclude:: ../../doc/misc/mirror.zoneopt +.. literalinclude:: ../../doc/misc/forward.zoneopt +.. literalinclude:: ../../doc/misc/hint.zoneopt +.. literalinclude:: ../../doc/misc/redirect.zoneopt +.. literalinclude:: ../../doc/misc/static-stub.zoneopt +.. literalinclude:: ../../doc/misc/stub.zoneopt +.. literalinclude:: ../../doc/misc/delegation-only.zoneopt +.. literalinclude:: ../../doc/misc/in-view.zoneopt + +Files +~~~~~ + +|named_conf| + +See Also +~~~~~~~~ + +:iscman:`named(8) `, :iscman:`named-checkconf(8) `, :iscman:`rndc(8) `, :iscman:`rndc-confgen(8) `, :iscman:`tsig-keygen(8) `, BIND 9 Administrator Reference Manual. + diff --git a/bin/named/named.rst b/bin/named/named.rst new file mode 100644 index 0000000..dc6e46d --- /dev/null +++ b/bin/named/named.rst @@ -0,0 +1,254 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: named +.. program:: named +.. _man_named: + +named - Internet domain name server +----------------------------------- + +Synopsis +~~~~~~~~ + +:program:`named` [ [**-4**] | [**-6**] ] [**-c** config-file] [**-C**] [**-d** debug-level] [**-D** string] [**-E** engine-name] [**-f**] [**-g**] [**-L** logfile] [**-M** option] [**-m** flag] [**-n** #cpus] [**-p** port] [**-s**] [**-t** directory] [**-U** #listeners] [**-u** user] [**-v**] [**-V**] [**-X** lock-file] + +Description +~~~~~~~~~~~ + +:program:`named` is a Domain Name System (DNS) server, part of the BIND 9 +distribution from ISC. For more information on the DNS, see :rfc:`1033`, +:rfc:`1034`, and :rfc:`1035`. + +When invoked without arguments, :program:`named` reads the default +configuration file |named_conf|, reads any initial data, and +listens for queries. + +Options +~~~~~~~ + +.. option:: -4 + + This option tells :program:`named` to use only IPv4, even if the host machine is capable of IPv6. :option:`-4` and + :option:`-6` are mutually exclusive. + +.. option:: -6 + + This option tells :program:`named` to use only IPv6, even if the host machine is capable of IPv4. :option:`-4` and + :option:`-6` are mutually exclusive. + +.. option:: -c config-file + + This option tells :program:`named` to use ``config-file`` as its configuration file instead of the default, + |named_conf|. To ensure that the configuration file + can be reloaded after the server has changed its working directory + due to to a possible ``directory`` option in the configuration file, + ``config-file`` should be an absolute pathname. + +.. option:: -C + + This option prints out the default built-in configuration and exits. + + NOTE: This is for debugging purposes only and is not an + accurate representation of the actual configuration used by :iscman:`named` + at runtime. + +.. option:: -d debug-level + + This option sets the daemon's debug level to ``debug-level``. Debugging traces from + :program:`named` become more verbose as the debug level increases. + +.. option:: -D string + + This option specifies a string that is used to identify a instance of :program:`named` + in a process listing. The contents of ``string`` are not examined. + +.. option:: -E engine-name + + When applicable, this option specifies the hardware to use for cryptographic + operations, such as a secure key store used for signing. + + When BIND 9 is built with OpenSSL, this needs to be set to the OpenSSL + engine identifier that drives the cryptographic accelerator or + hardware service module (usually ``pkcs11``). + +.. option:: -f + + This option runs the server in the foreground (i.e., do not daemonize). + +.. option:: -g + + This option runs the server in the foreground and forces all logging to ``stderr``. + +.. option:: -L logfile + + This option sets the log to the file ``logfile`` by default, instead of the system log. + +.. option:: -M option + + This option sets the default (comma-separated) memory context + options. The possible flags are: + + - ``fill``: fill blocks of memory with tag values when they are + allocated or freed, to assist debugging of memory problems; this is + the implicit default if :program:`named` has been compiled with + ``--enable-developer``. + + - ``nofill``: disable the behavior enabled by ``fill``; this is the + implicit default unless :program:`named` has been compiled with + ``--enable-developer``. + +.. option:: -m flag + + This option turns on memory usage debugging flags. Possible flags are ``usage``, + ``trace``, ``record``, ``size``, and ``mctx``. These correspond to the + ``ISC_MEM_DEBUGXXXX`` flags described in ````. + +.. option:: -n #cpus + + This option creates ``#cpus`` worker threads to take advantage of multiple CPUs. If + not specified, :program:`named` tries to determine the number of CPUs + present and creates one thread per CPU. If it is unable to determine + the number of CPUs, a single worker thread is created. + +.. option:: -p value + + This option specifies the port(s) on which the server will listen + for queries. If ``value`` is of the form ```` or + ``dns=``, the server will listen for DNS queries on + ``portnum``; if not not specified, the default is port 53. If + ``value`` is of the form ``tls=``, the server will + listen for TLS queries on ``portnum``; the default is 853. + If ``value`` is of the form ``https=``, the server will + listen for HTTPS queries on ``portnum``; the default is 443. + If ``value`` is of the form ``http=``, the server will + listen for HTTP queries on ``portnum``; the default is 80. + +.. option:: -s + + This option writes memory usage statistics to ``stdout`` on exit. + +.. note:: + + This option is mainly of interest to BIND 9 developers and may be + removed or changed in a future release. + +.. option:: -S #max-socks + + This option is deprecated and no longer has any function. + +.. warning:: + + This option should be unnecessary for the vast majority of users. + The use of this option could even be harmful, because the specified + value may exceed the limitation of the underlying system API. It + is therefore set only when the default configuration causes + exhaustion of file descriptors and the operational environment is + known to support the specified number of sockets. Note also that + the actual maximum number is normally slightly fewer than the + specified value, because :program:`named` reserves some file descriptors + for its internal use. + +.. option:: -t directory + + This option tells :program:`named` to chroot to ``directory`` after processing the command-line arguments, but + before reading the configuration file. + +.. warning:: + + This option should be used in conjunction with the :option:`-u` option, + as chrooting a process running as root doesn't enhance security on + most systems; the way ``chroot`` is defined allows a process + with root privileges to escape a chroot jail. + +.. option:: -U #listeners + + This option tells :program:`named` the number of ``#listeners`` worker threads to listen on, for incoming UDP packets on + each address. If not specified, :program:`named` calculates a default + value based on the number of detected CPUs: 1 for 1 CPU, and the + number of detected CPUs minus one for machines with more than 1 CPU. + This cannot be increased to a value higher than the number of CPUs. + If :option:`-n` has been set to a higher value than the number of detected + CPUs, then :option:`-U` may be increased as high as that value, but no + higher. + +.. option:: -u user + + This option sets the setuid to ``user`` after completing privileged operations, such as + creating sockets that listen on privileged ports. + +.. note:: + + On Linux, :program:`named` uses the kernel's capability mechanism to drop + all root privileges except the ability to ``bind`` to a + privileged port and set process resource limits. Unfortunately, + this means that the :option:`-u` option only works when :program:`named` is run + on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or later, since + previous kernels did not allow privileges to be retained after + ``setuid``. + +.. option:: -v + + This option reports the version number and exits. + +.. option:: -V + + This option reports the version number, build options, supported + cryptographics algorithms, and exits. + +.. option:: -X lock-file + + This option acquires a lock on the specified file at runtime; this helps to + prevent duplicate :program:`named` instances from running simultaneously. + Use of this option overrides the ``lock-file`` option in + :iscman:`named.conf`. If set to ``none``, the lock file check is disabled. + +Signals +~~~~~~~ + +In routine operation, signals should not be used to control the +nameserver; :iscman:`rndc` should be used instead. + +SIGHUP + This signal forces a reload of the server. + +SIGINT, SIGTERM + These signals shut down the server. + +The result of sending any other signals to the server is undefined. + +Configuration +~~~~~~~~~~~~~ + +The :program:`named` configuration file is too complex to describe in detail +here. A complete description is provided in the BIND 9 Administrator +Reference Manual. + +:program:`named` inherits the ``umask`` (file creation mode mask) from the +parent process. If files created by :program:`named`, such as journal files, +need to have custom permissions, the ``umask`` should be set explicitly +in the script used to start the :program:`named` process. + +Files +~~~~~ + +|named_conf| + The default configuration file. + +|named_pid| + The default process-id file. + +See Also +~~~~~~~~ + +:rfc:`1033`, :rfc:`1034`, :rfc:`1035`, :iscman:`named-checkconf(8) `, :iscman:`named-checkzone(8) `, :iscman:`rndc(8) `, :iscman:`named.conf(5) `, BIND 9 Administrator Reference Manual. diff --git a/bin/named/os.c b/bin/named/os.c new file mode 100644 index 0000000..7af4729 --- /dev/null +++ b/bin/named/os.c @@ -0,0 +1,932 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ +#include +#include +#include +#include /* dev_t FreeBSD 2.1 */ +#ifdef HAVE_UNAME +#include +#endif /* ifdef HAVE_UNAME */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TZSET +#include +#endif /* ifdef HAVE_TZSET */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef HAVE_LIBSCF +#include +#endif /* ifdef HAVE_LIBSCF */ + +static char *pidfile = NULL; +static char *lockfile = NULL; +static int devnullfd = -1; +static int singletonfd = -1; + +#ifndef ISC_FACILITY +#define ISC_FACILITY LOG_DAEMON +#endif /* ifndef ISC_FACILITY */ + +static struct passwd *runas_pw = NULL; +static bool done_setuid = false; +static int dfd[2] = { -1, -1 }; + +#ifdef HAVE_SYS_CAPABILITY_H + +static bool non_root = false; +static bool non_root_caps = false; + +#include +#include + +static void +linux_setcaps(cap_t caps) { + char strbuf[ISC_STRERRORSIZE]; + + if ((getuid() != 0 && !non_root_caps) || non_root) { + return; + } + if (cap_set_proc(caps) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("cap_set_proc() failed: %s:" + " please ensure that the capset kernel" + " module is loaded. see insmod(8)", + strbuf); + } +} + +#define SET_CAP(flag) \ + do { \ + cap_flag_value_t curval; \ + capval = (flag); \ + err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \ + if (err != -1 && curval) { \ + err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, \ + CAP_SET); \ + if (err == -1) { \ + strerror_r(errno, strbuf, sizeof(strbuf)); \ + named_main_earlyfatal("cap_set_proc failed: " \ + "%s", \ + strbuf); \ + } \ + \ + err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, \ + CAP_SET); \ + if (err == -1) { \ + strerror_r(errno, strbuf, sizeof(strbuf)); \ + named_main_earlyfatal("cap_set_proc failed: " \ + "%s", \ + strbuf); \ + } \ + } \ + } while (0) +#define INIT_CAP \ + do { \ + caps = cap_init(); \ + if (caps == NULL) { \ + strerror_r(errno, strbuf, sizeof(strbuf)); \ + named_main_earlyfatal("cap_init failed: %s", strbuf); \ + } \ + curcaps = cap_get_proc(); \ + if (curcaps == NULL) { \ + strerror_r(errno, strbuf, sizeof(strbuf)); \ + named_main_earlyfatal("cap_get_proc failed: %s", \ + strbuf); \ + } \ + } while (0) +#define FREE_CAP \ + { \ + cap_free(caps); \ + cap_free(curcaps); \ + } \ + while (0) + +static void +linux_initialprivs(void) { + cap_t caps; + cap_t curcaps; + cap_value_t capval; + char strbuf[ISC_STRERRORSIZE]; + int err; + + /*% + * We don't need most privileges, so we drop them right away. + * Later on linux_minprivs() will be called, which will drop our + * capabilities to the minimum needed to run the server. + */ + INIT_CAP; + + /* + * We need to be able to bind() to privileged ports, notably port 53! + */ + SET_CAP(CAP_NET_BIND_SERVICE); + + /* + * We need chroot() initially too. + */ + SET_CAP(CAP_SYS_CHROOT); + + /* + * We need setuid() as the kernel supports keeping capabilities after + * setuid(). + */ + SET_CAP(CAP_SETUID); + + /* + * Since we call initgroups, we need this. + */ + SET_CAP(CAP_SETGID); + + /* + * Without this, we run into problems reading a configuration file + * owned by a non-root user and non-world-readable on startup. + */ + SET_CAP(CAP_DAC_READ_SEARCH); + + /* + * XXX We might want to add CAP_SYS_RESOURCE, though it's not + * clear it would work right given the way linuxthreads work. + * XXXDCL But since we need to be able to set the maximum number + * of files, the stack size, data size, and core dump size to + * support named.conf options, this is now being added to test. + */ + SET_CAP(CAP_SYS_RESOURCE); + + /* + * We need to be able to set the ownership of the containing + * directory of the pid file when we create it. + */ + SET_CAP(CAP_CHOWN); + + linux_setcaps(caps); + + FREE_CAP; +} + +static void +linux_minprivs(void) { + cap_t caps; + cap_t curcaps; + cap_value_t capval; + char strbuf[ISC_STRERRORSIZE]; + int err; + + INIT_CAP; + /*% + * Drop all privileges except the ability to bind() to privileged + * ports. + * + * It's important that we drop CAP_SYS_CHROOT. If we didn't, it + * chroot() could be used to escape from the chrooted area. + */ + + SET_CAP(CAP_NET_BIND_SERVICE); + + /* + * XXX We might want to add CAP_SYS_RESOURCE, though it's not + * clear it would work right given the way linuxthreads work. + * XXXDCL But since we need to be able to set the maximum number + * of files, the stack size, data size, and core dump size to + * support named.conf options, this is now being added to test. + */ + SET_CAP(CAP_SYS_RESOURCE); + + linux_setcaps(caps); + + FREE_CAP; +} + +static void +linux_keepcaps(void) { + char strbuf[ISC_STRERRORSIZE]; + /*% + * Ask the kernel to allow us to keep our capabilities after we + * setuid(). + */ + + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { + if (errno != EINVAL) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("prctl() failed: %s", strbuf); + } + } else { + non_root_caps = true; + if (getuid() != 0) { + non_root = true; + } + } +} + +#endif /* HAVE_SYS_CAPABILITY_H */ + +static void +setup_syslog(const char *progname) { + int options; + + options = LOG_PID; +#ifdef LOG_NDELAY + options |= LOG_NDELAY; +#endif /* ifdef LOG_NDELAY */ + openlog(isc_file_basename(progname), options, ISC_FACILITY); +} + +void +named_os_init(const char *progname) { + setup_syslog(progname); +#ifdef HAVE_SYS_CAPABILITY_H + linux_initialprivs(); +#endif /* ifdef HAVE_SYS_CAPABILITY_H */ +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_IGN); +#endif /* ifdef SIGXFSZ */ +} + +void +named_os_daemonize(void) { + pid_t pid; + char strbuf[ISC_STRERRORSIZE]; + + if (pipe(dfd) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("pipe(): %s", strbuf); + } + + pid = fork(); + if (pid == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("fork(): %s", strbuf); + } + if (pid != 0) { + int n; + /* + * Wait for the child to finish loading for the first time. + * This would be so much simpler if fork() worked once we + * were multi-threaded. + */ + (void)close(dfd[1]); + do { + char buf; + n = read(dfd[0], &buf, 1); + if (n == 1) { + _exit(0); + } + } while (n == -1 && errno == EINTR); + _exit(1); + } + (void)close(dfd[0]); + + /* + * We're the child. + */ + + if (setsid() == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("setsid(): %s", strbuf); + } + + /* + * Try to set stdin, stdout, and stderr to /dev/null, but press + * on even if it fails. + * + * XXXMLG The close() calls here are unneeded on all but NetBSD, but + * are harmless to include everywhere. dup2() is supposed to close + * the FD if it is in use, but unproven-pthreads-0.16 is broken + * and will end up closing the wrong FD. This will be fixed eventually, + * and these calls will be removed. + */ + if (devnullfd != -1) { + if (devnullfd != STDIN_FILENO) { + (void)close(STDIN_FILENO); + (void)dup2(devnullfd, STDIN_FILENO); + } + if (devnullfd != STDOUT_FILENO) { + (void)close(STDOUT_FILENO); + (void)dup2(devnullfd, STDOUT_FILENO); + } + if (devnullfd != STDERR_FILENO && !named_g_keepstderr) { + (void)close(STDERR_FILENO); + (void)dup2(devnullfd, STDERR_FILENO); + } + } +} + +void +named_os_started(void) { + char buf = 0; + + /* + * Signal to the parent that we started successfully. + */ + if (dfd[0] != -1 && dfd[1] != -1) { + if (write(dfd[1], &buf, 1) != 1) { + named_main_earlyfatal("unable to signal parent that we " + "otherwise started " + "successfully."); + } + close(dfd[1]); + dfd[0] = dfd[1] = -1; + } +} + +void +named_os_opendevnull(void) { + devnullfd = open("/dev/null", O_RDWR, 0); +} + +void +named_os_closedevnull(void) { + if (devnullfd != STDIN_FILENO && devnullfd != STDOUT_FILENO && + devnullfd != STDERR_FILENO) + { + close(devnullfd); + devnullfd = -1; + } +} + +static bool +all_digits(const char *s) { + if (*s == '\0') { + return (false); + } + while (*s != '\0') { + if (!isdigit((unsigned char)(*s))) { + return (false); + } + s++; + } + return (true); +} + +void +named_os_chroot(const char *root) { + char strbuf[ISC_STRERRORSIZE]; +#ifdef HAVE_LIBSCF + named_smf_chroot = 0; +#endif /* ifdef HAVE_LIBSCF */ + if (root != NULL) { +#ifdef HAVE_CHROOT + if (chroot(root) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("chroot(): %s", strbuf); + } +#else /* ifdef HAVE_CHROOT */ + named_main_earlyfatal("chroot(): disabled"); +#endif /* ifdef HAVE_CHROOT */ + if (chdir("/") < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("chdir(/): %s", strbuf); + } +#ifdef HAVE_LIBSCF + /* Set named_smf_chroot flag on successful chroot. */ + named_smf_chroot = 1; +#endif /* ifdef HAVE_LIBSCF */ + } +} + +void +named_os_inituserinfo(const char *username) { + if (username == NULL) { + return; + } + + if (all_digits(username)) { + runas_pw = getpwuid((uid_t)atoi(username)); + } else { + runas_pw = getpwnam(username); + } + endpwent(); + + if (runas_pw == NULL) { + named_main_earlyfatal("user '%s' unknown", username); + } + + if (getuid() == 0) { + char strbuf[ISC_STRERRORSIZE]; + if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("initgroups(): %s", strbuf); + } + } +} + +void +named_os_changeuser(void) { + char strbuf[ISC_STRERRORSIZE]; + if (runas_pw == NULL || done_setuid) { + return; + } + + done_setuid = true; + + if (setgid(runas_pw->pw_gid) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("setgid(): %s", strbuf); + } + + if (setuid(runas_pw->pw_uid) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("setuid(): %s", strbuf); + } + +#if defined(HAVE_SYS_CAPABILITY_H) + /* + * Restore the ability of named to drop core after the setuid() + * call has disabled it. + */ + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s", + strbuf); + } + + linux_minprivs(); +#endif /* if defined(HAVE_SYS_CAPABILITY_H) */ +} + +uid_t +ns_os_uid(void) { + if (runas_pw == NULL) { + return (0); + } + return (runas_pw->pw_uid); +} + +void +named_os_adjustnofile(void) { +#if defined(__linux__) || defined(__sun) + isc_result_t result; + isc_resourcevalue_t newvalue; + + /* + * Linux: max number of open files specified by one thread doesn't seem + * to apply to other threads on Linux. + * Sun: restriction needs to be removed sooner when hundreds of CPUs + * are available. + */ + newvalue = ISC_RESOURCE_UNLIMITED; + + result = isc_resource_setlimit(isc_resource_openfiles, newvalue); + if (result != ISC_R_SUCCESS) { + named_main_earlywarning("couldn't adjust limit on open files"); + } +#endif /* if defined(__linux__) || defined(__sun) */ +} + +void +named_os_minprivs(void) { +#if defined(HAVE_SYS_CAPABILITY_H) + linux_keepcaps(); + named_os_changeuser(); + linux_minprivs(); +#endif /* if defined(HAVE_SYS_CAPABILITY_H) */ +} + +static int +safe_open(const char *filename, mode_t mode, bool append) { + int fd; + struct stat sb; + + if (stat(filename, &sb) == -1) { + if (errno != ENOENT) { + return (-1); + } + } else if ((sb.st_mode & S_IFREG) == 0) { + errno = EOPNOTSUPP; + return (-1); + } + + if (append) { + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode); + } else { + if (unlink(filename) < 0 && errno != ENOENT) { + return (-1); + } + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode); + } + return (fd); +} + +static void +cleanup_pidfile(void) { + int n; + if (pidfile != NULL) { + n = unlink(pidfile); + if (n == -1 && errno != ENOENT) { + named_main_earlywarning("unlink '%s': failed", pidfile); + } + free(pidfile); + } + pidfile = NULL; +} + +static void +cleanup_lockfile(void) { + if (singletonfd != -1) { + close(singletonfd); + singletonfd = -1; + } + + if (lockfile != NULL) { + int n = unlink(lockfile); + if (n == -1 && errno != ENOENT) { + named_main_earlywarning("unlink '%s': failed", + lockfile); + } + free(lockfile); + lockfile = NULL; + } +} + +/* + * Ensure that a directory exists. + * NOTE: This function overwrites the '/' characters in 'filename' with + * nulls. The caller should copy the filename to a fresh buffer first. + */ +static int +mkdirpath(char *filename, void (*report)(const char *, ...)) { + char *slash = strrchr(filename, '/'); + char strbuf[ISC_STRERRORSIZE]; + unsigned int mode; + + if (slash != NULL && slash != filename) { + struct stat sb; + *slash = '\0'; + + if (stat(filename, &sb) == -1) { + if (errno != ENOENT) { + strerror_r(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't stat '%s': %s", filename, + strbuf); + goto error; + } + if (mkdirpath(filename, report) == -1) { + goto error; + } + /* + * Handle "//", "/./" and "/../" in path. + */ + if (!strcmp(slash + 1, "") || !strcmp(slash + 1, ".") || + !strcmp(slash + 1, "..")) + { + *slash = '/'; + return (0); + } + mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */ + mode |= S_IRGRP | S_IXGRP; /* g=rx */ + mode |= S_IROTH | S_IXOTH; /* o=rx */ + if (mkdir(filename, mode) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't mkdir '%s': %s", filename, + strbuf); + goto error; + } + if (runas_pw != NULL && + chown(filename, runas_pw->pw_uid, + runas_pw->pw_gid) == -1) + { + strerror_r(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't chown '%s': %s", filename, + strbuf); + } + } + *slash = '/'; + } + return (0); + +error: + *slash = '/'; + return (-1); +} + +#if !HAVE_SYS_CAPABILITY_H +static void +setperms(uid_t uid, gid_t gid) { +#if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID) + char strbuf[ISC_STRERRORSIZE]; +#endif /* if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID) */ +#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) + gid_t oldgid, tmpg; +#endif /* if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */ +#if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) + uid_t olduid, tmpu; +#endif /* if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID) */ +#if defined(HAVE_SETEGID) + if (getegid() != gid && setegid(gid) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("unable to set effective " + "gid to %ld: %s", + (long)gid, strbuf); + } +#elif defined(HAVE_SETRESGID) + if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) { + if (setresgid(-1, gid, -1) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("unable to set effective " + "gid to %d: %s", + gid, strbuf); + } + } +#endif /* if defined(HAVE_SETEGID) */ + +#if defined(HAVE_SETEUID) + if (geteuid() != uid && seteuid(uid) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("unable to set effective " + "uid to %ld: %s", + (long)uid, strbuf); + } +#elif defined(HAVE_SETRESUID) + if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) { + if (setresuid(-1, uid, -1) == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("unable to set effective " + "uid to %d: %s", + uid, strbuf); + } + } +#endif /* if defined(HAVE_SETEUID) */ +} +#endif /* !HAVE_SYS_CAPABILITY_H */ + +FILE * +named_os_openfile(const char *filename, mode_t mode, bool switch_user) { + char strbuf[ISC_STRERRORSIZE], *f; + FILE *fp; + int fd; + + /* + * Make the containing directory if it doesn't exist. + */ + f = strdup(filename); + if (f == NULL) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("couldn't strdup() '%s': %s", filename, + strbuf); + return (NULL); + } + if (mkdirpath(f, named_main_earlywarning) == -1) { + free(f); + return (NULL); + } + free(f); + + if (switch_user && runas_pw != NULL) { + uid_t olduid = getuid(); + gid_t oldgid = getgid(); +#if HAVE_SYS_CAPABILITY_H + REQUIRE(olduid == runas_pw->pw_uid); + REQUIRE(oldgid == runas_pw->pw_gid); +#else /* HAVE_SYS_CAPABILITY_H */ + /* Set UID/GID to the one we'll be running with eventually */ + setperms(runas_pw->pw_uid, runas_pw->pw_gid); +#endif + fd = safe_open(filename, mode, false); + +#if !HAVE_SYS_CAPABILITY_H + /* Restore UID/GID to previous uid/gid */ + setperms(olduid, oldgid); +#endif + + if (fd == -1) { + fd = safe_open(filename, mode, false); + if (fd != -1) { + named_main_earlywarning("Required root " + "permissions to open " + "'%s'.", + filename); + } else { + named_main_earlywarning("Could not open " + "'%s'.", + filename); + } + named_main_earlywarning("Please check file and " + "directory permissions " + "or reconfigure the filename."); + } + } else { + fd = safe_open(filename, mode, false); + } + + if (fd < 0) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("could not open file '%s': %s", + filename, strbuf); + return (NULL); + } + + fp = fdopen(fd, "w"); + if (fp == NULL) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlywarning("could not fdopen() file '%s': %s", + filename, strbuf); + } + + return (fp); +} + +void +named_os_writepidfile(const char *filename, bool first_time) { + FILE *fh; + pid_t pid; + char strbuf[ISC_STRERRORSIZE]; + void (*report)(const char *, ...); + + /* + * The caller must ensure any required synchronization. + */ + + report = first_time ? named_main_earlyfatal : named_main_earlywarning; + + cleanup_pidfile(); + + if (filename == NULL) { + return; + } + + pidfile = strdup(filename); + if (pidfile == NULL) { + strerror_r(errno, strbuf, sizeof(strbuf)); + (*report)("couldn't strdup() '%s': %s", filename, strbuf); + return; + } + + fh = named_os_openfile(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, + first_time); + if (fh == NULL) { + cleanup_pidfile(); + return; + } + pid = getpid(); + if (fprintf(fh, "%ld\n", (long)pid) < 0) { + (*report)("fprintf() to pid file '%s' failed", filename); + (void)fclose(fh); + cleanup_pidfile(); + return; + } + if (fflush(fh) == EOF) { + (*report)("fflush() to pid file '%s' failed", filename); + (void)fclose(fh); + cleanup_pidfile(); + return; + } + (void)fclose(fh); +} + +bool +named_os_issingleton(const char *filename) { + char strbuf[ISC_STRERRORSIZE]; + struct flock lock; + + if (singletonfd != -1) { + return (true); + } + + if (strcasecmp(filename, "none") == 0) { + return (true); + } + + /* + * Make the containing directory if it doesn't exist. + */ + lockfile = strdup(filename); + if (lockfile == NULL) { + strerror_r(errno, strbuf, sizeof(strbuf)); + named_main_earlyfatal("couldn't allocate memory for '%s': %s", + filename, strbuf); + } else { + int ret = mkdirpath(lockfile, named_main_earlywarning); + if (ret == -1) { + named_main_earlywarning("couldn't create '%s'", + filename); + cleanup_lockfile(); + return (false); + } + } + + /* + * named_os_openfile() uses safeopen() which removes any existing + * files. We can't use that here. + */ + singletonfd = open(filename, O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (singletonfd == -1) { + cleanup_lockfile(); + return (false); + } + + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 1; + + /* Non-blocking (does not wait for lock) */ + if (fcntl(singletonfd, F_SETLK, &lock) == -1) { + close(singletonfd); + singletonfd = -1; + return (false); + } + + return (true); +} + +void +named_os_shutdown(void) { + closelog(); + cleanup_pidfile(); + cleanup_lockfile(); +} + +void +named_os_shutdownmsg(char *command, isc_buffer_t *text) { + char *last, *ptr; + pid_t pid; + + /* Skip the command name. */ + if (strtok_r(command, " \t", &last) == NULL) { + return; + } + + if ((ptr = strtok_r(NULL, " \t", &last)) == NULL) { + return; + } + + if (strcmp(ptr, "-p") != 0) { + return; + } + + pid = getpid(); + + (void)isc_buffer_printf(text, "pid: %ld", (long)pid); +} + +void +named_os_tzset(void) { +#ifdef HAVE_TZSET + tzset(); +#endif /* ifdef HAVE_TZSET */ +} + +#ifdef HAVE_UNAME +static char unamebuf[sizeof(struct utsname)]; +#else +static const char unamebuf[] = { "unknown architecture" }; +#endif +static const char *unamep = NULL; + +static void +getuname(void) { +#ifdef HAVE_UNAME + struct utsname uts; + + memset(&uts, 0, sizeof(uts)); + if (uname(&uts) < 0) { + snprintf(unamebuf, sizeof(unamebuf), "unknown architecture"); + return; + } + + snprintf(unamebuf, sizeof(unamebuf), "%s %s %s %s", uts.sysname, + uts.machine, uts.release, uts.version); +#endif /* ifdef HAVE_UNAME */ + unamep = unamebuf; +} + +const char * +named_os_uname(void) { + if (unamep == NULL) { + getuname(); + } + return (unamep); +} diff --git a/bin/named/server.c b/bin/named/server.c new file mode 100644 index 0000000..2f21fc5 --- /dev/null +++ b/bin/named/server.c @@ -0,0 +1,16755 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DNSTAP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#if defined(HAVE_GEOIP2) +#include +#endif /* HAVE_GEOIP2 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBSCF +#include + +#include +#endif /* ifdef HAVE_LIBSCF */ + +#ifdef HAVE_LMDB +#include +#define count_newzones count_newzones_db +#define configure_newzones configure_newzones_db +#define dumpzone dumpzone_db +#else /* HAVE_LMDB */ +#define count_newzones count_newzones_file +#define configure_newzones configure_newzones_file +#define dumpzone dumpzone_file +#endif /* HAVE_LMDB */ + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)-1) +#endif /* ifndef SIZE_MAX */ + +#ifndef SIZE_AS_PERCENT +#define SIZE_AS_PERCENT ((size_t)-2) +#endif /* ifndef SIZE_AS_PERCENT */ + +#ifdef TUNE_LARGE +#define RESOLVER_NTASKS_PERCPU 32 +#else +#define RESOLVER_NTASKS_PERCPU 8 +#endif /* TUNE_LARGE */ + +/* RFC7828 defines timeout as 16-bit value specified in units of 100 + * milliseconds, so the maximum and minimum advertised and keepalive + * timeouts are capped by the data type (it's ~109 minutes) + */ +#define MIN_INITIAL_TIMEOUT UINT32_C(2500) /* 2.5 seconds */ +#define MAX_INITIAL_TIMEOUT UINT32_C(120000) /* 2 minutes */ +#define MIN_IDLE_TIMEOUT UINT32_C(100) /* 0.1 seconds */ +#define MAX_IDLE_TIMEOUT UINT32_C(120000) /* 2 minutes */ +#define MIN_KEEPALIVE_TIMEOUT UINT32_C(100) /* 0.1 seconds */ +#define MAX_KEEPALIVE_TIMEOUT UINT32_C(UINT16_MAX * 100) +#define MIN_ADVERTISED_TIMEOUT UINT32_C(0) /* No minimum */ +#define MAX_ADVERTISED_TIMEOUT UINT32_C(UINT16_MAX * 100) + +/*% + * Check an operation for failure. Assumes that the function + * using it has a 'result' variable and a 'cleanup' label. + */ +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +#define TCHECK(op) \ + do { \ + tresult = (op); \ + if (tresult != ISC_R_SUCCESS) { \ + isc_buffer_clear(*text); \ + goto cleanup; \ + } \ + } while (0) + +#define CHECKM(op, msg) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \ + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, \ + "%s: %s", msg, \ + isc_result_totext(result)); \ + goto cleanup; \ + } \ + } while (0) + +#define CHECKMF(op, msg, file) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \ + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, \ + "%s '%s': %s", msg, file, \ + isc_result_totext(result)); \ + goto cleanup; \ + } \ + } while (0) + +#define CHECKFATAL(op, msg) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + fatal(server, msg, result); \ + } while (0) + +/*% + * Maximum ADB size for views that share a cache. Use this limit to suppress + * the total of memory footprint, which should be the main reason for sharing + * a cache. Only effective when a finite max-cache-size is specified. + * This is currently defined to be 8MB. + */ +#define MAX_ADB_SIZE_FOR_CACHESHARE 8388608U + +struct named_dispatch { + isc_sockaddr_t addr; + unsigned int dispatchgen; + dns_dispatch_t *dispatch; + ISC_LINK(struct named_dispatch) link; +}; + +struct named_cache { + dns_cache_t *cache; + dns_view_t *primaryview; + bool needflush; + bool adbsizeadjusted; + dns_rdataclass_t rdclass; + ISC_LINK(named_cache_t) link; +}; + +struct dumpcontext { + isc_mem_t *mctx; + bool dumpcache; + bool dumpzones; + bool dumpadb; + bool dumpbad; + bool dumpexpired; + bool dumpfail; + FILE *fp; + ISC_LIST(struct viewlistentry) viewlist; + struct viewlistentry *view; + struct zonelistentry *zone; + dns_dumpctx_t *mdctx; + dns_db_t *db; + dns_db_t *cache; + isc_task_t *task; + dns_dbversion_t *version; +}; + +struct viewlistentry { + dns_view_t *view; + ISC_LINK(struct viewlistentry) link; + ISC_LIST(struct zonelistentry) zonelist; +}; + +struct zonelistentry { + dns_zone_t *zone; + ISC_LINK(struct zonelistentry) link; +}; + +/*% + * Configuration context to retain for each view that allows + * new zones to be added at runtime. + */ +typedef struct ns_cfgctx { + isc_mem_t *mctx; + cfg_parser_t *conf_parser; + cfg_parser_t *add_parser; + cfg_obj_t *config; + cfg_obj_t *vconfig; + cfg_obj_t *nzf_config; + cfg_aclconfctx_t *actx; +} ns_cfgctx_t; + +/*% + * A function to write out added-zone configuration to the new_zone_file + * specified in 'view'. Maybe called by delete_zoneconf(). + */ +typedef isc_result_t (*nzfwriter_t)(const cfg_obj_t *config, dns_view_t *view); + +/*% + * Holds state information for the initial zone loading process. + * Uses the isc_refcount structure to count the number of views + * with pending zone loads, dereferencing as each view finishes. + */ +typedef struct { + named_server_t *server; + bool reconfig; + isc_refcount_t refs; +} ns_zoneload_t; + +typedef struct { + named_server_t *server; +} catz_cb_data_t; + +typedef struct catz_chgzone_event { + ISC_EVENT_COMMON(struct catz_chgzone_event); + dns_catz_entry_t *entry; + dns_catz_zone_t *origin; + dns_view_t *view; + catz_cb_data_t *cbd; + bool mod; +} catz_chgzone_event_t; + +typedef struct { + unsigned int magic; +#define DZARG_MAGIC ISC_MAGIC('D', 'z', 'a', 'r') + isc_buffer_t **text; + isc_result_t result; +} ns_dzarg_t; + +/* + * These zones should not leak onto the Internet. + */ +const char *empty_zones[] = { + /* RFC 1918 */ + "10.IN-ADDR.ARPA", "16.172.IN-ADDR.ARPA", "17.172.IN-ADDR.ARPA", + "18.172.IN-ADDR.ARPA", "19.172.IN-ADDR.ARPA", "20.172.IN-ADDR.ARPA", + "21.172.IN-ADDR.ARPA", "22.172.IN-ADDR.ARPA", "23.172.IN-ADDR.ARPA", + "24.172.IN-ADDR.ARPA", "25.172.IN-ADDR.ARPA", "26.172.IN-ADDR.ARPA", + "27.172.IN-ADDR.ARPA", "28.172.IN-ADDR.ARPA", "29.172.IN-ADDR.ARPA", + "30.172.IN-ADDR.ARPA", "31.172.IN-ADDR.ARPA", "168.192.IN-ADDR.ARPA", + + /* RFC 6598 */ + "64.100.IN-ADDR.ARPA", "65.100.IN-ADDR.ARPA", "66.100.IN-ADDR.ARPA", + "67.100.IN-ADDR.ARPA", "68.100.IN-ADDR.ARPA", "69.100.IN-ADDR.ARPA", + "70.100.IN-ADDR.ARPA", "71.100.IN-ADDR.ARPA", "72.100.IN-ADDR.ARPA", + "73.100.IN-ADDR.ARPA", "74.100.IN-ADDR.ARPA", "75.100.IN-ADDR.ARPA", + "76.100.IN-ADDR.ARPA", "77.100.IN-ADDR.ARPA", "78.100.IN-ADDR.ARPA", + "79.100.IN-ADDR.ARPA", "80.100.IN-ADDR.ARPA", "81.100.IN-ADDR.ARPA", + "82.100.IN-ADDR.ARPA", "83.100.IN-ADDR.ARPA", "84.100.IN-ADDR.ARPA", + "85.100.IN-ADDR.ARPA", "86.100.IN-ADDR.ARPA", "87.100.IN-ADDR.ARPA", + "88.100.IN-ADDR.ARPA", "89.100.IN-ADDR.ARPA", "90.100.IN-ADDR.ARPA", + "91.100.IN-ADDR.ARPA", "92.100.IN-ADDR.ARPA", "93.100.IN-ADDR.ARPA", + "94.100.IN-ADDR.ARPA", "95.100.IN-ADDR.ARPA", "96.100.IN-ADDR.ARPA", + "97.100.IN-ADDR.ARPA", "98.100.IN-ADDR.ARPA", "99.100.IN-ADDR.ARPA", + "100.100.IN-ADDR.ARPA", "101.100.IN-ADDR.ARPA", "102.100.IN-ADDR.ARPA", + "103.100.IN-ADDR.ARPA", "104.100.IN-ADDR.ARPA", "105.100.IN-ADDR.ARPA", + "106.100.IN-ADDR.ARPA", "107.100.IN-ADDR.ARPA", "108.100.IN-ADDR.ARPA", + "109.100.IN-ADDR.ARPA", "110.100.IN-ADDR.ARPA", "111.100.IN-ADDR.ARPA", + "112.100.IN-ADDR.ARPA", "113.100.IN-ADDR.ARPA", "114.100.IN-ADDR.ARPA", + "115.100.IN-ADDR.ARPA", "116.100.IN-ADDR.ARPA", "117.100.IN-ADDR.ARPA", + "118.100.IN-ADDR.ARPA", "119.100.IN-ADDR.ARPA", "120.100.IN-ADDR.ARPA", + "121.100.IN-ADDR.ARPA", "122.100.IN-ADDR.ARPA", "123.100.IN-ADDR.ARPA", + "124.100.IN-ADDR.ARPA", "125.100.IN-ADDR.ARPA", "126.100.IN-ADDR.ARPA", + "127.100.IN-ADDR.ARPA", + + /* RFC 5735 and RFC 5737 */ + "0.IN-ADDR.ARPA", /* THIS NETWORK */ + "127.IN-ADDR.ARPA", /* LOOPBACK */ + "254.169.IN-ADDR.ARPA", /* LINK LOCAL */ + "2.0.192.IN-ADDR.ARPA", /* TEST NET */ + "100.51.198.IN-ADDR.ARPA", /* TEST NET 2 */ + "113.0.203.IN-ADDR.ARPA", /* TEST NET 3 */ + "255.255.255.255.IN-ADDR.ARPA", /* BROADCAST */ + + /* Local IPv6 Unicast Addresses */ + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6." + "ARPA", + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6." + "ARPA", + /* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */ + "D.F.IP6.ARPA", "8.E.F.IP6.ARPA", /* LINK LOCAL */ + "9.E.F.IP6.ARPA", /* LINK LOCAL */ + "A.E.F.IP6.ARPA", /* LINK LOCAL */ + "B.E.F.IP6.ARPA", /* LINK LOCAL */ + + /* Example Prefix, RFC 3849. */ + "8.B.D.0.1.0.0.2.IP6.ARPA", + + /* RFC 7534 */ + "EMPTY.AS112.ARPA", + + /* RFC 8375 */ + "HOME.ARPA", + + NULL +}; + +noreturn static void +fatal(named_server_t *server, const char *msg, isc_result_t result); + +static void +named_server_reload(isc_task_t *task, isc_event_t *event); + +#ifdef HAVE_LIBNGHTTP2 +static isc_result_t +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, + isc_mem_t *mctx, ns_listenelt_t **target); +#endif + +static isc_result_t +listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target); + +static isc_result_t +listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, + ns_listenlist_t **target); + +static isc_result_t +configure_forward(const cfg_obj_t *config, dns_view_t *view, + const dns_name_t *origin, const cfg_obj_t *forwarders, + const cfg_obj_t *forwardtype); + +static isc_result_t +configure_alternates(const cfg_obj_t *config, dns_view_t *view, + const cfg_obj_t *alternates); + +static isc_result_t +configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, + const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, + dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, + cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, + bool modify); + +static void +configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig, + dns_view_t *view); + +static isc_result_t +configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, + isc_mem_t *mctx, cfg_aclconfctx_t *actx); + +static isc_result_t +add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx); + +static void +end_reserved_dispatches(named_server_t *server, bool all); + +static void +newzone_cfgctx_destroy(void **cfgp); + +static isc_result_t +putstr(isc_buffer_t **b, const char *str); + +static isc_result_t +putmem(isc_buffer_t **b, const char *str, size_t len); + +static isc_result_t +putuint8(isc_buffer_t **b, uint8_t val); + +static isc_result_t +putnull(isc_buffer_t **b); + +static int +count_zones(const cfg_obj_t *conf); + +#ifdef HAVE_LMDB +static isc_result_t +migrate_nzf(dns_view_t *view); + +static isc_result_t +nzd_writable(dns_view_t *view); + +static isc_result_t +nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi); + +static isc_result_t +nzd_env_reopen(dns_view_t *view); + +static void +nzd_env_close(dns_view_t *view); + +static isc_result_t +nzd_close(MDB_txn **txnp, bool commit); + +static isc_result_t +nzd_count(dns_view_t *view, int *countp); +#else /* ifdef HAVE_LMDB */ +static isc_result_t +nzf_append(dns_view_t *view, const cfg_obj_t *zconfig); +#endif /* ifdef HAVE_LMDB */ + +/*% + * Configure a single view ACL at '*aclp'. Get its configuration from + * 'vconfig' (for per-view configuration) and maybe from 'config' + */ +static isc_result_t +configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config, + const cfg_obj_t *gconfig, const char *aclname, + const char *acltuplename, cfg_aclconfctx_t *actx, + isc_mem_t *mctx, dns_acl_t **aclp) { + isc_result_t result; + const cfg_obj_t *maps[4]; + const cfg_obj_t *aclobj = NULL; + int i = 0; + + if (*aclp != NULL) { + dns_acl_detach(aclp); + } + if (vconfig != NULL) { + maps[i++] = cfg_tuple_get(vconfig, "options"); + } + if (config != NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + if (gconfig != NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(gconfig, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + maps[i] = NULL; + + (void)named_config_get(maps, aclname, &aclobj); + if (aclobj == NULL) { + /* + * No value available. *aclp == NULL. + */ + return (ISC_R_SUCCESS); + } + + if (acltuplename != NULL) { + /* + * If the ACL is given in an optional tuple, retrieve it. + * The parser should have ensured that a valid object be + * returned. + */ + aclobj = cfg_tuple_get(aclobj, acltuplename); + } + + result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, mctx, 0, + aclp); + + return (result); +} + +/*% + * Configure a sortlist at '*aclp'. Essentially the same as + * configure_view_acl() except it calls cfg_acl_fromconfig with a + * nest_level value of 2. + */ +static isc_result_t +configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, + dns_acl_t **aclp) { + isc_result_t result; + const cfg_obj_t *maps[3]; + const cfg_obj_t *aclobj = NULL; + int i = 0; + + if (*aclp != NULL) { + dns_acl_detach(aclp); + } + if (vconfig != NULL) { + maps[i++] = cfg_tuple_get(vconfig, "options"); + } + if (config != NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + maps[i] = NULL; + + (void)named_config_get(maps, "sortlist", &aclobj); + if (aclobj == NULL) { + return (ISC_R_SUCCESS); + } + + /* + * Use a nest level of 3 for the "top level" of the sortlist; + * this means each entry in the top three levels will be stored + * as lists of separate, nested ACLs, rather than merged together + * into IP tables as is usually done with ACLs. + */ + result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, mctx, 3, + aclp); + + return (result); +} + +static isc_result_t +configure_view_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config, + const char *confname, const char *conftuplename, + isc_mem_t *mctx, dns_rbt_t **rbtp) { + isc_result_t result; + const cfg_obj_t *maps[3]; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *element; + int i = 0; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + const char *str; + const cfg_obj_t *nameobj; + + if (*rbtp != NULL) { + dns_rbt_destroy(rbtp); + } + if (vconfig != NULL) { + maps[i++] = cfg_tuple_get(vconfig, "options"); + } + if (config != NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + maps[i] = NULL; + + (void)named_config_get(maps, confname, &obj); + if (obj == NULL) { + /* + * No value available. *rbtp == NULL. + */ + return (ISC_R_SUCCESS); + } + + if (conftuplename != NULL) { + obj = cfg_tuple_get(obj, conftuplename); + if (cfg_obj_isvoid(obj)) { + return (ISC_R_SUCCESS); + } + } + + result = dns_rbt_create(mctx, NULL, NULL, rbtp); + if (result != ISC_R_SUCCESS) { + return (result); + } + + name = dns_fixedname_initname(&fixed); + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + nameobj = cfg_listelt_value(element); + str = cfg_obj_asstring(nameobj); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + /* + * We don't need the node data, but need to set dummy data to + * avoid a partial match with an empty node. For example, if + * we have foo.example.com and bar.example.com, we'd get a match + * for baz.example.com, which is not the expected result. + * We simply use (void *)1 as the dummy data. + */ + result = dns_rbt_addname(*rbtp, name, (void *)1); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(nameobj, named_g_lctx, ISC_LOG_ERROR, + "failed to add %s for %s: %s", str, + confname, isc_result_totext(result)); + goto cleanup; + } + } + + return (result); + +cleanup: + dns_rbt_destroy(rbtp); + return (result); +} + +static isc_result_t +ta_fromconfig(const cfg_obj_t *key, bool *initialp, const char **namestrp, + unsigned char *digest, dns_rdata_ds_t *ds) { + isc_result_t result; + dns_rdata_dnskey_t keystruct; + dns_rdata_t rdata = DNS_RDATA_INIT; + uint32_t rdata1, rdata2, rdata3; + const char *datastr = NULL, *namestr = NULL; + unsigned char data[4096]; + isc_buffer_t databuf; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; + isc_region_t r; + dns_fixedname_t fname; + dns_name_t *name = NULL; + isc_buffer_t namebuf; + const char *atstr = NULL; + enum { + INIT_DNSKEY, + STATIC_DNSKEY, + INIT_DS, + STATIC_DS, + TRUSTED + } anchortype; + + REQUIRE(namestrp != NULL && *namestrp == NULL); + REQUIRE(ds != NULL); + + /* if DNSKEY, flags; if DS, key tag */ + rdata1 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata1")); + + /* if DNSKEY, protocol; if DS, algorithm */ + rdata2 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata2")); + + /* if DNSKEY, algorithm; if DS, digest type */ + rdata3 = cfg_obj_asuint32(cfg_tuple_get(key, "rdata3")); + + namestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + *namestrp = namestr; + + name = dns_fixedname_initname(&fname); + isc_buffer_constinit(&namebuf, namestr, strlen(namestr)); + isc_buffer_add(&namebuf, strlen(namestr)); + CHECK(dns_name_fromtext(name, &namebuf, dns_rootname, 0, NULL)); + + if (*initialp) { + atstr = cfg_obj_asstring(cfg_tuple_get(key, "anchortype")); + + if (strcasecmp(atstr, "static-key") == 0) { + *initialp = false; + anchortype = STATIC_DNSKEY; + } else if (strcasecmp(atstr, "static-ds") == 0) { + *initialp = false; + anchortype = STATIC_DS; + } else if (strcasecmp(atstr, "initial-key") == 0) { + anchortype = INIT_DNSKEY; + } else if (strcasecmp(atstr, "initial-ds") == 0) { + anchortype = INIT_DS; + } else { + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "key '%s': " + "invalid initialization method '%s'", + namestr, atstr); + result = ISC_R_FAILURE; + goto cleanup; + } + } else { + anchortype = TRUSTED; + } + + isc_buffer_init(&databuf, data, sizeof(data)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + *ds = (dns_rdata_ds_t){ .common.rdclass = dns_rdataclass_in, + .common.rdtype = dns_rdatatype_ds }; + + ISC_LINK_INIT(&ds->common, link); + + switch (anchortype) { + case INIT_DNSKEY: + case STATIC_DNSKEY: + case TRUSTED: + /* + * This function should never be reached for view + * class other than IN + */ + keystruct.common.rdclass = dns_rdataclass_in; + keystruct.common.rdtype = dns_rdatatype_dnskey; + + /* + * The key data in keystruct is not dynamically allocated. + */ + keystruct.mctx = NULL; + + ISC_LINK_INIT(&keystruct.common, link); + + if (rdata1 > 0xffff) { + CHECKM(ISC_R_RANGE, "key flags"); + } + if (rdata1 & DNS_KEYFLAG_REVOKE) { + CHECKM(DST_R_BADKEYTYPE, "key flags revoke bit set"); + } + if (rdata2 > 0xff) { + CHECKM(ISC_R_RANGE, "key protocol"); + } + if (rdata3 > 0xff) { + CHECKM(ISC_R_RANGE, "key algorithm"); + } + + keystruct.flags = (uint16_t)rdata1; + keystruct.protocol = (uint8_t)rdata2; + keystruct.algorithm = (uint8_t)rdata3; + + if (!dst_algorithm_supported(keystruct.algorithm)) { + CHECK(DST_R_UNSUPPORTEDALG); + } + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_base64_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + keystruct.datalen = r.length; + keystruct.data = r.base; + + CHECK(dns_rdata_fromstruct(&rdata, keystruct.common.rdclass, + keystruct.common.rdtype, &keystruct, + &rrdatabuf)); + CHECK(dns_ds_fromkeyrdata(name, &rdata, DNS_DSDIGEST_SHA256, + digest, ds)); + break; + + case INIT_DS: + case STATIC_DS: + if (rdata1 > 0xffff) { + CHECKM(ISC_R_RANGE, "key tag"); + } + if (rdata2 > 0xff) { + CHECKM(ISC_R_RANGE, "key algorithm"); + } + if (rdata3 > 0xff) { + CHECKM(ISC_R_RANGE, "digest type"); + } + + ds->key_tag = (uint16_t)rdata1; + ds->algorithm = (uint8_t)rdata2; + ds->digest_type = (uint8_t)rdata3; + + datastr = cfg_obj_asstring(cfg_tuple_get(key, "data")); + CHECK(isc_hex_decodestring(datastr, &databuf)); + isc_buffer_usedregion(&databuf, &r); + + switch (ds->digest_type) { + case DNS_DSDIGEST_SHA1: + if (r.length != ISC_SHA1_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA256: + if (r.length != ISC_SHA256_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + case DNS_DSDIGEST_SHA384: + if (r.length != ISC_SHA384_DIGESTLENGTH) { + CHECK(ISC_R_UNEXPECTEDEND); + } + break; + default: + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "key '%s': " + "unknown ds digest type %u", + namestr, ds->digest_type); + result = ISC_R_FAILURE; + goto cleanup; + break; + } + + ds->length = r.length; + ds->digest = digest; + memmove(ds->digest, r.base, r.length); + + break; + + default: + UNREACHABLE(); + } + + return (ISC_R_SUCCESS); + +cleanup: + return (result); +} + +static void +sfd_add(const dns_name_t *name, void *arg) { + if (arg != NULL) { + dns_view_sfd_add(arg, name); + } +} + +/*% + * Parse 'key' in the context of view configuration 'vconfig'. If successful, + * add the key to 'secroots' if both of the following conditions are true: + * + * - 'keyname_match' is NULL or it matches the owner name of 'key', + * - support for the algorithm used by 'key' is not disabled by 'resolver' + * for the owner name of 'key'. + * + * 'managed' is true for managed keys and false for trusted keys. 'mctx' is + * the memory context to use for allocating memory. + */ +static isc_result_t +process_key(const cfg_obj_t *key, dns_keytable_t *secroots, + const dns_name_t *keyname_match, dns_view_t *view, bool managed) { + dns_fixedname_t fkeyname; + dns_name_t *keyname = NULL; + const char *namestr = NULL; + dns_rdata_ds_t ds; + isc_result_t result; + bool initializing = managed; + unsigned char digest[ISC_MAX_MD_SIZE]; + isc_buffer_t b; + + result = ta_fromconfig(key, &initializing, &namestr, digest, &ds); + + switch (result) { + case ISC_R_SUCCESS: + /* + * Trust anchor was parsed correctly. + */ + isc_buffer_constinit(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + keyname = dns_fixedname_initname(&fkeyname); + result = dns_name_fromtext(keyname, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + break; + case DST_R_UNSUPPORTEDALG: + case DST_R_BADKEYTYPE: + /* + * Key was parsed correctly, but it cannot be used; this is not + * a fatal error - log a warning about this key being ignored, + * but do not prevent any further ones from being processed. + */ + cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, + "ignoring %s for '%s': %s", + initializing ? "initial-key" : "static-key", + namestr, isc_result_totext(result)); + return (ISC_R_SUCCESS); + case DST_R_NOCRYPTO: + /* + * Crypto support is not available. + */ + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "ignoring %s for '%s': no crypto support", + initializing ? "initial-key" : "static-key", + namestr); + return (result); + default: + /* + * Something unexpected happened; we have no choice but to + * indicate an error so that the configuration loading process + * is interrupted. + */ + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "configuring %s for '%s': %s", + initializing ? "initial-key" : "static-key", + namestr, isc_result_totext(result)); + return (ISC_R_FAILURE); + } + + /* + * If the caller requested to only load keys for a specific name and + * the owner name of this key does not match the requested name, do not + * load it. + */ + if (keyname_match != NULL && !dns_name_equal(keyname_match, keyname)) { + goto done; + } + + /* + * Ensure that 'resolver' allows using the algorithm of this key for + * its owner name. If it does not, do not load the key and log a + * warning, but do not prevent further keys from being processed. + */ + if (!dns_resolver_algorithm_supported(view->resolver, keyname, + ds.algorithm)) + { + cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, + "ignoring %s for '%s': algorithm is disabled", + initializing ? "initial-key" : "static-key", + namestr); + goto done; + } + + /* + * Add the key to 'secroots'. Keys from a "trust-anchors" or + * "managed-keys" statement may be either static or initializing + * keys. If it's not initializing, we don't want to treat it as + * managed, so we use 'initializing' twice here, for both the + * 'managed' and 'initializing' arguments to dns_keytable_add(). + */ + result = dns_keytable_add(secroots, initializing, initializing, keyname, + &ds, sfd_add, view); + +done: + return (result); +} + +/* + * Load keys from configuration into key table. If 'keyname' is specified, + * only load keys matching that name. If 'managed' is true, load the key as + * an initializing key. + */ +static isc_result_t +load_view_keys(const cfg_obj_t *keys, dns_view_t *view, bool managed, + const dns_name_t *keyname) { + const cfg_listelt_t *elt, *elt2; + const cfg_obj_t *keylist; + isc_result_t result; + dns_keytable_t *secroots = NULL; + + CHECK(dns_view_getsecroots(view, &secroots)); + + for (elt = cfg_list_first(keys); elt != NULL; elt = cfg_list_next(elt)) + { + keylist = cfg_listelt_value(elt); + + for (elt2 = cfg_list_first(keylist); elt2 != NULL; + elt2 = cfg_list_next(elt2)) + { + CHECK(process_key(cfg_listelt_value(elt2), secroots, + keyname, view, managed)); + } + } + +cleanup: + if (secroots != NULL) { + dns_keytable_detach(&secroots); + } + if (result == DST_R_NOCRYPTO) { + result = ISC_R_SUCCESS; + } + return (result); +} + +/*% + * Check whether a key has been successfully loaded. + */ +static bool +keyloaded(dns_view_t *view, const dns_name_t *name) { + isc_result_t result; + dns_keytable_t *secroots = NULL; + dns_keynode_t *keynode = NULL; + + result = dns_view_getsecroots(view, &secroots); + if (result != ISC_R_SUCCESS) { + return (false); + } + + result = dns_keytable_find(secroots, name, &keynode); + + if (keynode != NULL) { + dns_keytable_detachkeynode(secroots, &keynode); + } + if (secroots != NULL) { + dns_keytable_detach(&secroots); + } + + return (result == ISC_R_SUCCESS); +} + +/*% + * Configure DNSSEC keys for a view. + * + * The per-view configuration values and the server-global defaults are read + * from 'vconfig' and 'config'. + */ +static isc_result_t +configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig, + const cfg_obj_t *config, const cfg_obj_t *bindkeys, + bool auto_root, isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *view_keys = NULL; + const cfg_obj_t *global_keys = NULL; + const cfg_obj_t *view_managed_keys = NULL; + const cfg_obj_t *view_trust_anchors = NULL; + const cfg_obj_t *global_managed_keys = NULL; + const cfg_obj_t *global_trust_anchors = NULL; + const cfg_obj_t *maps[4]; + const cfg_obj_t *voptions = NULL; + const cfg_obj_t *options = NULL; + const cfg_obj_t *obj = NULL; + const char *directory; + int i = 0; + + /* We don't need trust anchors for the _bind view */ + if (strcmp(view->name, "_bind") == 0 && + view->rdclass == dns_rdataclass_chaos) + { + return (ISC_R_SUCCESS); + } + + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + if (voptions != NULL) { + (void)cfg_map_get(voptions, "trusted-keys", &view_keys); + + /* managed-keys and trust-anchors are synonyms. */ + (void)cfg_map_get(voptions, "managed-keys", + &view_managed_keys); + (void)cfg_map_get(voptions, "trust-anchors", + &view_trust_anchors); + + maps[i++] = voptions; + } + } + + if (config != NULL) { + (void)cfg_map_get(config, "trusted-keys", &global_keys); + + /* managed-keys and trust-anchors are synonyms. */ + (void)cfg_map_get(config, "managed-keys", &global_managed_keys); + (void)cfg_map_get(config, "trust-anchors", + &global_trust_anchors); + + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + + maps[i++] = named_g_defaults; + maps[i] = NULL; + + result = dns_view_initsecroots(view, mctx); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "couldn't create keytable"); + return (ISC_R_UNEXPECTED); + } + + result = dns_view_initntatable(view, named_g_taskmgr, named_g_timermgr); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "couldn't create NTA table"); + return (ISC_R_UNEXPECTED); + } + + if (auto_root && view->rdclass == dns_rdataclass_in) { + const cfg_obj_t *builtin_keys = NULL; + + /* + * If bind.keys exists and is populated, it overrides + * the trust-anchors clause hard-coded in named_g_config. + */ + if (bindkeys != NULL) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "obtaining root key for view %s " + "from '%s'", + view->name, named_g_server->bindkeysfile); + + (void)cfg_map_get(bindkeys, "trust-anchors", + &builtin_keys); + + if (builtin_keys == NULL) { + isc_log_write( + named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "dnssec-validation auto: " + "WARNING: root zone key " + "not found"); + } + } + + if (builtin_keys == NULL) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "using built-in root key for view %s", + view->name); + + (void)cfg_map_get(named_g_config, "trust-anchors", + &builtin_keys); + } + + if (builtin_keys != NULL) { + CHECK(load_view_keys(builtin_keys, view, true, + dns_rootname)); + } + + if (!keyloaded(view, dns_rootname)) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "root key not loaded"); + result = ISC_R_FAILURE; + goto cleanup; + } + } + + if (view->rdclass == dns_rdataclass_in) { + CHECK(load_view_keys(view_keys, view, false, NULL)); + CHECK(load_view_keys(view_trust_anchors, view, true, NULL)); + CHECK(load_view_keys(view_managed_keys, view, true, NULL)); + + CHECK(load_view_keys(global_keys, view, false, NULL)); + CHECK(load_view_keys(global_trust_anchors, view, true, NULL)); + CHECK(load_view_keys(global_managed_keys, view, true, NULL)); + } + + /* + * Add key zone for managed keys. + */ + obj = NULL; + (void)named_config_get(maps, "managed-keys-directory", &obj); + directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL); + if (directory != NULL) { + result = isc_file_isdirectory(directory); + } + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "invalid managed-keys-directory %s: %s", + directory, isc_result_totext(result)); + goto cleanup; + } else if (directory != NULL) { + if (!isc_file_isdirwritable(directory)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "managed-keys-directory '%s' " + "is not writable", + directory); + result = ISC_R_NOPERM; + goto cleanup; + } + } + + CHECK(add_keydata_zone(view, directory, named_g_mctx)); + +cleanup: + return (result); +} + +static isc_result_t +mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver) { + const cfg_listelt_t *element; + const cfg_obj_t *obj; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + bool value; + isc_result_t result; + isc_buffer_t b; + + name = dns_fixedname_initname(&fixed); + for (element = cfg_list_first(mbs); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + value = cfg_obj_asboolean(cfg_tuple_get(obj, "value")); + CHECK(dns_resolver_setmustbesecure(resolver, name, value)); + } + + result = ISC_R_SUCCESS; + +cleanup: + return (result); +} + +/*% + * Get a dispatch appropriate for the resolver of a given view. + */ +static isc_result_t +get_view_querysource_dispatch(const cfg_obj_t **maps, int af, + dns_dispatch_t **dispatchp, bool is_firstview) { + isc_result_t result = ISC_R_FAILURE; + dns_dispatch_t *disp = NULL; + isc_sockaddr_t sa; + const cfg_obj_t *obj = NULL; + + switch (af) { + case AF_INET: + result = named_config_get(maps, "query-source", &obj); + INSIST(result == ISC_R_SUCCESS); + break; + case AF_INET6: + result = named_config_get(maps, "query-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS); + break; + default: + UNREACHABLE(); + } + + sa = *(cfg_obj_assockaddr(obj)); + INSIST(isc_sockaddr_pf(&sa) == af); + + /* + * If we don't support this address family, we're done! + */ + switch (af) { + case AF_INET: + result = isc_net_probeipv4(); + break; + case AF_INET6: + result = isc_net_probeipv6(); + break; + default: + UNREACHABLE(); + } + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + /* + * Try to find a dispatcher that we can share. + */ + if (isc_sockaddr_getport(&sa) != 0) { + INSIST(obj != NULL); + if (is_firstview) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO, + "using specific query-source port " + "suppresses port randomization and can be " + "insecure."); + } + } + + result = dns_dispatch_createudp(named_g_dispatchmgr, &sa, &disp); + if (result != ISC_R_SUCCESS) { + isc_sockaddr_t any; + char buf[ISC_SOCKADDR_FORMATSIZE]; + + switch (af) { + case AF_INET: + isc_sockaddr_any(&any); + break; + case AF_INET6: + isc_sockaddr_any6(&any); + break; + } + if (isc_sockaddr_equal(&sa, &any)) { + return (ISC_R_SUCCESS); + } + isc_sockaddr_format(&sa, buf, sizeof(buf)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "could not get query source dispatcher (%s)", + buf); + return (result); + } + + *dispatchp = disp; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +configure_order(dns_order_t *order, const cfg_obj_t *ent) { + dns_rdataclass_t rdclass; + dns_rdatatype_t rdtype; + const cfg_obj_t *obj; + dns_fixedname_t fixed; + unsigned int mode = 0; + const char *str; + isc_buffer_t b; + isc_result_t result; + bool addroot; + + result = named_config_getclass(cfg_tuple_get(ent, "class"), + dns_rdataclass_any, &rdclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = named_config_gettype(cfg_tuple_get(ent, "type"), + dns_rdatatype_any, &rdtype); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = cfg_tuple_get(ent, "name"); + if (cfg_obj_isstring(obj)) { + str = cfg_obj_asstring(obj); + } else { + str = "*"; + } + addroot = (strcmp(str, "*") == 0); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + dns_fixedname_init(&fixed); + result = dns_name_fromtext(dns_fixedname_name(&fixed), &b, dns_rootname, + 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = cfg_tuple_get(ent, "ordering"); + INSIST(cfg_obj_isstring(obj)); + str = cfg_obj_asstring(obj); + if (!strcasecmp(str, "fixed")) { +#if DNS_RDATASET_FIXED + mode = DNS_RDATASETATTR_FIXEDORDER; +#else /* if DNS_RDATASET_FIXED */ + mode = DNS_RDATASETATTR_CYCLIC; +#endif /* DNS_RDATASET_FIXED */ + } else if (!strcasecmp(str, "random")) { + mode = DNS_RDATASETATTR_RANDOMIZE; + } else if (!strcasecmp(str, "cyclic")) { + mode = DNS_RDATASETATTR_CYCLIC; + } else if (!strcasecmp(str, "none")) { + mode = DNS_RDATASETATTR_NONE; + } else { + UNREACHABLE(); + } + + /* + * "*" should match everything including the root (BIND 8 compat). + * As dns_name_matcheswildcard(".", "*.") returns FALSE add a + * explicit entry for "." when the name is "*". + */ + if (addroot) { + result = dns_order_add(order, dns_rootname, rdtype, rdclass, + mode); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (dns_order_add(order, dns_fixedname_name(&fixed), rdtype, + rdclass, mode)); +} + +static isc_result_t +configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { + isc_netaddr_t na; + dns_peer_t *peer; + const cfg_obj_t *obj; + const char *str; + isc_result_t result; + unsigned int prefixlen; + + cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen); + + peer = NULL; + result = dns_peer_newprefix(mctx, &na, prefixlen, &peer); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "bogus", &obj); + if (obj != NULL) { + CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "provide-ixfr", &obj); + if (obj != NULL) { + CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "request-expire", &obj); + if (obj != NULL) { + CHECK(dns_peer_setrequestexpire(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "request-ixfr", &obj); + if (obj != NULL) { + CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "request-nsid", &obj); + if (obj != NULL) { + CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "send-cookie", &obj); + if (obj != NULL) { + CHECK(dns_peer_setsendcookie(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "edns", &obj); + if (obj != NULL) { + CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "edns-udp-size", &obj); + if (obj != NULL) { + uint32_t udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512U) { + udpsize = 512U; + } + if (udpsize > 4096U) { + udpsize = 4096U; + } + CHECK(dns_peer_setudpsize(peer, (uint16_t)udpsize)); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "edns-version", &obj); + if (obj != NULL) { + uint32_t ednsversion = cfg_obj_asuint32(obj); + if (ednsversion > 255U) { + ednsversion = 255U; + } + CHECK(dns_peer_setednsversion(peer, (uint8_t)ednsversion)); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "max-udp-size", &obj); + if (obj != NULL) { + uint32_t udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512U) { + udpsize = 512U; + } + if (udpsize > 4096U) { + udpsize = 4096U; + } + CHECK(dns_peer_setmaxudp(peer, (uint16_t)udpsize)); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "padding", &obj); + if (obj != NULL) { + uint32_t padding = cfg_obj_asuint32(obj); + if (padding > 512U) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "server padding value cannot " + "exceed 512: lowering"); + padding = 512U; + } + CHECK(dns_peer_setpadding(peer, (uint16_t)padding)); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "tcp-only", &obj); + if (obj != NULL) { + CHECK(dns_peer_setforcetcp(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "tcp-keepalive", &obj); + if (obj != NULL) { + CHECK(dns_peer_settcpkeepalive(peer, cfg_obj_asboolean(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "transfers", &obj); + if (obj != NULL) { + CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj))); + } + + obj = NULL; + (void)cfg_map_get(cpeer, "transfer-format", &obj); + if (obj != NULL) { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "many-answers") == 0) { + CHECK(dns_peer_settransferformat(peer, + dns_many_answers)); + } else if (strcasecmp(str, "one-answer") == 0) { + CHECK(dns_peer_settransferformat(peer, dns_one_answer)); + } else { + UNREACHABLE(); + } + } + + obj = NULL; + (void)cfg_map_get(cpeer, "keys", &obj); + if (obj != NULL) { + result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj)); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + + obj = NULL; + if (na.family == AF_INET) { + (void)cfg_map_get(cpeer, "transfer-source", &obj); + } else { + (void)cfg_map_get(cpeer, "transfer-source-v6", &obj); + } + if (obj != NULL) { + result = dns_peer_settransfersource(peer, + cfg_obj_assockaddr(obj)); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + } + + obj = NULL; + if (na.family == AF_INET) { + (void)cfg_map_get(cpeer, "notify-source", &obj); + } else { + (void)cfg_map_get(cpeer, "notify-source-v6", &obj); + } + if (obj != NULL) { + result = dns_peer_setnotifysource(peer, + cfg_obj_assockaddr(obj)); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + } + + obj = NULL; + if (na.family == AF_INET) { + (void)cfg_map_get(cpeer, "query-source", &obj); + } else { + (void)cfg_map_get(cpeer, "query-source-v6", &obj); + } + if (obj != NULL) { + result = dns_peer_setquerysource(peer, cfg_obj_assockaddr(obj)); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + } + + *peerp = peer; + return (ISC_R_SUCCESS); + +cleanup: + dns_peer_detach(&peer); + return (result); +} + +static isc_result_t +configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx, + const dns_dyndbctx_t *dctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *obj; + const char *name, *library; + + /* Get the name of the dyndb instance and the library path . */ + name = cfg_obj_asstring(cfg_tuple_get(dyndb, "name")); + library = cfg_obj_asstring(cfg_tuple_get(dyndb, "library")); + + obj = cfg_tuple_get(dyndb, "parameters"); + if (obj != NULL) { + result = dns_dyndb_load(library, name, cfg_obj_asstring(obj), + cfg_obj_file(obj), cfg_obj_line(obj), + mctx, dctx); + } + + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dynamic database '%s' configuration failed: %s", + name, isc_result_totext(result)); + } + return (result); +} + +static isc_result_t +disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) { + isc_result_t result; + const cfg_obj_t *algorithms; + const cfg_listelt_t *element; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + + name = dns_fixedname_initname(&fixed); + str = cfg_obj_asstring(cfg_tuple_get(disabled, "name")); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + + algorithms = cfg_tuple_get(disabled, "algorithms"); + for (element = cfg_list_first(algorithms); element != NULL; + element = cfg_list_next(element)) + { + isc_textregion_t r; + dns_secalg_t alg; + + DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); + r.length = strlen(r.base); + + result = dns_secalg_fromtext(&alg, &r); + if (result != ISC_R_SUCCESS) { + uint8_t ui; + result = isc_parse_uint8(&ui, r.base, 10); + alg = ui; + } + if (result != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), named_g_lctx, + ISC_LOG_ERROR, "invalid algorithm"); + CHECK(result); + } + CHECK(dns_resolver_disable_algorithm(resolver, name, alg)); + } +cleanup: + return (result); +} + +static isc_result_t +disable_ds_digests(const cfg_obj_t *disabled, dns_resolver_t *resolver) { + isc_result_t result; + const cfg_obj_t *digests; + const cfg_listelt_t *element; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + + name = dns_fixedname_initname(&fixed); + str = cfg_obj_asstring(cfg_tuple_get(disabled, "name")); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + + digests = cfg_tuple_get(disabled, "digests"); + for (element = cfg_list_first(digests); element != NULL; + element = cfg_list_next(element)) + { + isc_textregion_t r; + dns_dsdigest_t digest; + + DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); + r.length = strlen(r.base); + + /* disable_ds_digests handles numeric values. */ + result = dns_dsdigest_fromtext(&digest, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), named_g_lctx, + ISC_LOG_ERROR, "invalid algorithm"); + CHECK(result); + } + CHECK(dns_resolver_disable_ds_digest(resolver, name, digest)); + } +cleanup: + return (result); +} + +static bool +on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) { + const cfg_listelt_t *element; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + const cfg_obj_t *value; + const char *str; + isc_buffer_t b; + + name = dns_fixedname_initname(&fixed); + + for (element = cfg_list_first(disablelist); element != NULL; + element = cfg_list_next(element)) + { + value = cfg_listelt_value(element); + str = cfg_obj_asstring(value); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (dns_name_equal(name, zonename)) { + return (true); + } + } + return (false); +} + +static isc_result_t +check_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv, + isc_mem_t *mctx) { + char **argv = NULL; + unsigned int i; + isc_result_t result = ISC_R_SUCCESS; + + CHECK(dns_zone_getdbtype(zone, &argv, mctx)); + + /* + * Check that all the arguments match. + */ + for (i = 0; i < dbtypec; i++) { + if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) { + CHECK(ISC_R_FAILURE); + + /* + * Check that there are not extra arguments. + */ + } + } + + /* + * Check that there are not extra arguments. + */ + if (i == dbtypec && argv[i] != NULL) { + result = ISC_R_FAILURE; + } + +cleanup: + isc_mem_free(mctx, argv); + return (result); +} + +static isc_result_t +setquerystats(dns_zone_t *zone, isc_mem_t *mctx, dns_zonestat_level_t level) { + isc_result_t result; + isc_stats_t *zoneqrystats; + + dns_zone_setstatlevel(zone, level); + + zoneqrystats = NULL; + if (level == dns_zonestat_full) { + result = isc_stats_create(mctx, &zoneqrystats, + ns_statscounter_max); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + dns_zone_setrequeststats(zone, zoneqrystats); + if (zoneqrystats != NULL) { + isc_stats_detach(&zoneqrystats); + } + + return (ISC_R_SUCCESS); +} + +static named_cache_t * +cachelist_find(named_cachelist_t *cachelist, const char *cachename, + dns_rdataclass_t rdclass) { + named_cache_t *nsc; + + for (nsc = ISC_LIST_HEAD(*cachelist); nsc != NULL; + nsc = ISC_LIST_NEXT(nsc, link)) + { + if (nsc->rdclass == rdclass && + strcmp(dns_cache_getname(nsc->cache), cachename) == 0) + { + return (nsc); + } + } + + return (NULL); +} + +static bool +cache_reusable(dns_view_t *originview, dns_view_t *view, + bool new_zero_no_soattl) { + if (originview->rdclass != view->rdclass || + originview->checknames != view->checknames || + dns_resolver_getzeronosoattl(originview->resolver) != + new_zero_no_soattl || + originview->acceptexpired != view->acceptexpired || + originview->enablevalidation != view->enablevalidation || + originview->maxcachettl != view->maxcachettl || + originview->maxncachettl != view->maxncachettl) + { + return (false); + } + + return (true); +} + +static bool +cache_sharable(dns_view_t *originview, dns_view_t *view, + bool new_zero_no_soattl, uint64_t new_max_cache_size, + uint32_t new_stale_ttl, uint32_t new_stale_refresh_time) { + /* + * If the cache cannot even reused for the same view, it cannot be + * shared with other views. + */ + if (!cache_reusable(originview, view, new_zero_no_soattl)) { + return (false); + } + + /* + * Check other cache related parameters that must be consistent among + * the sharing views. + */ + if (dns_cache_getservestalettl(originview->cache) != new_stale_ttl || + dns_cache_getservestalerefresh(originview->cache) != + new_stale_refresh_time || + dns_cache_getcachesize(originview->cache) != new_max_cache_size) + { + return (false); + } + + return (true); +} + +/* + * Callback from DLZ configure when the driver sets up a writeable zone + */ +static isc_result_t +dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) { + dns_name_t *origin = dns_zone_getorigin(zone); + dns_rdataclass_t zclass = view->rdclass; + isc_result_t result; + + result = dns_zonemgr_managezone(named_g_server->zonemgr, zone); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_zone_setstats(zone, named_g_server->zonestats); + + return (named_zone_configure_writeable_dlz(dlzdb, zone, zclass, + origin)); +} + +static isc_result_t +dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na, + unsigned int prefixlen, const char *server, const char *contact) { + char reverse[48 + sizeof("ip6.arpa.")] = { 0 }; + char buf[sizeof("x.x.")]; + const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." }; + const char *sep = ": view "; + const char *viewname = view->name; + const unsigned char *s6; + dns_fixedname_t fixed; + dns_name_t *name; + dns_zone_t *zone = NULL; + int dns64_dbtypec = 4; + isc_buffer_t b; + isc_result_t result; + + REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 || + prefixlen == 56 || prefixlen == 64 || prefixlen == 96); + + if (!strcmp(viewname, "_default")) { + sep = ""; + viewname = ""; + } + + /* + * Construct the reverse name of the zone. + */ + s6 = na->type.in6.s6_addr; + while (prefixlen > 0) { + prefixlen -= 8; + snprintf(buf, sizeof(buf), "%x.%x.", s6[prefixlen / 8] & 0xf, + (s6[prefixlen / 8] >> 4) & 0xf); + strlcat(reverse, buf, sizeof(reverse)); + } + strlcat(reverse, "ip6.arpa.", sizeof(reverse)); + + /* + * Create the actual zone. + */ + if (server != NULL) { + dns64_dbtype[2] = server; + } + if (contact != NULL) { + dns64_dbtype[3] = contact; + } + name = dns_fixedname_initname(&fixed); + isc_buffer_constinit(&b, reverse, strlen(reverse)); + isc_buffer_add(&b, strlen(reverse)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + CHECK(dns_zone_create(&zone, mctx)); + CHECK(dns_zone_setorigin(zone, name)); + dns_zone_setview(zone, view); + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone)); + dns_zone_setclass(zone, view->rdclass); + dns_zone_settype(zone, dns_zone_primary); + dns_zone_setstats(zone, named_g_server->zonestats); + dns_zone_setdbtype(zone, dns64_dbtypec, dns64_dbtype); + if (view->queryacl != NULL) { + dns_zone_setqueryacl(zone, view->queryacl); + } + if (view->queryonacl != NULL) { + dns_zone_setqueryonacl(zone, view->queryonacl); + } + dns_zone_setdialup(zone, dns_dialuptype_no); + dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); + CHECK(setquerystats(zone, mctx, dns_zonestat_none)); /* XXXMPA */ + CHECK(dns_view_addzone(view, zone)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dns64 reverse zone%s%s: %s", sep, viewname, reverse); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + return (result); +} + +#ifdef USE_DNSRPS +typedef struct conf_dnsrps_ctx conf_dnsrps_ctx_t; +struct conf_dnsrps_ctx { + isc_result_t result; + char *cstr; + size_t cstr_size; + isc_mem_t *mctx; +}; + +/* + * Add to the DNSRPS configuration string. + */ +static bool +conf_dnsrps_sadd(conf_dnsrps_ctx_t *ctx, const char *p, ...) { + size_t new_len, cur_len, new_cstr_size; + char *new_cstr; + va_list args; + + if (ctx->cstr == NULL) { + ctx->cstr = isc_mem_get(ctx->mctx, 256); + ctx->cstr[0] = '\0'; + ctx->cstr_size = 256; + } + + cur_len = strlen(ctx->cstr); + va_start(args, p); + new_len = vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p, + args) + + 1; + va_end(args); + + if (cur_len + new_len <= ctx->cstr_size) { + return (true); + } + + new_cstr_size = ((cur_len + new_len) / 256 + 1) * 256; + new_cstr = isc_mem_get(ctx->mctx, new_cstr_size); + + memmove(new_cstr, ctx->cstr, cur_len); + isc_mem_put(ctx->mctx, ctx->cstr, ctx->cstr_size); + ctx->cstr_size = new_cstr_size; + ctx->cstr = new_cstr; + + /* cannot use args twice after a single va_start()on some systems */ + va_start(args, p); + vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p, args); + va_end(args); + return (true); +} + +/* + * Get an DNSRPS configuration value using the global and view options + * for the default. Return false upon failure. + */ +static bool +conf_dnsrps_get(const cfg_obj_t **sub_obj, const cfg_obj_t **maps, + const cfg_obj_t *obj, const char *name, + conf_dnsrps_ctx_t *ctx) { + if (ctx != NULL && ctx->result != ISC_R_SUCCESS) { + *sub_obj = NULL; + return (false); + } + + *sub_obj = cfg_tuple_get(obj, name); + if (cfg_obj_isvoid(*sub_obj)) { + *sub_obj = NULL; + if (maps != NULL && + ISC_R_SUCCESS != named_config_get(maps, name, sub_obj)) + { + *sub_obj = NULL; + } + } + return (true); +} + +/* + * Handle a DNSRPS boolean configuration value with the global and view + * options providing the default. + */ +static void +conf_dnsrps_yes_no(const cfg_obj_t *obj, const char *name, + conf_dnsrps_ctx_t *ctx) { + const cfg_obj_t *sub_obj; + + if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx)) { + return; + } + if (sub_obj == NULL) { + return; + } + if (ctx == NULL) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "\"%s\" without \"dnsrps-enable yes\"", name); + return; + } + + conf_dnsrps_sadd(ctx, " %s %s", name, + cfg_obj_asboolean(sub_obj) ? "yes" : "no"); +} + +static void +conf_dnsrps_num(const cfg_obj_t *obj, const char *name, + conf_dnsrps_ctx_t *ctx) { + const cfg_obj_t *sub_obj; + + if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx)) { + return; + } + if (sub_obj == NULL) { + return; + } + if (ctx == NULL) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "\"%s\" without \"dnsrps-enable yes\"", name); + return; + } + + if (cfg_obj_isduration(sub_obj)) { + conf_dnsrps_sadd(ctx, " %s %d", name, + cfg_obj_asduration(sub_obj)); + } else { + conf_dnsrps_sadd(ctx, " %s %d", name, + cfg_obj_asuint32(sub_obj)); + } +} + +/* + * Convert the parsed RPZ configuration statement to a string for + * dns_rpz_new_zones(). + */ +static isc_result_t +conf_dnsrps(dns_view_t *view, const cfg_obj_t **maps, bool nsip_enabled, + bool nsdname_enabled, dns_rpz_zbits_t *nsip_on, + dns_rpz_zbits_t *nsdname_on, char **rps_cstr, size_t *rps_cstr_size, + const cfg_obj_t *rpz_obj, const cfg_listelt_t *zone_element) { + conf_dnsrps_ctx_t ctx; + const cfg_obj_t *zone_obj, *obj; + dns_rpz_num_t rpz_num; + bool on; + const char *s; + + memset(&ctx, 0, sizeof(ctx)); + ctx.result = ISC_R_SUCCESS; + ctx.mctx = view->mctx; + + for (rpz_num = 0; zone_element != NULL && ctx.result == ISC_R_SUCCESS; + ++rpz_num) + { + zone_obj = cfg_listelt_value(zone_element); + + s = cfg_obj_asstring(cfg_tuple_get(zone_obj, "zone name")); + conf_dnsrps_sadd(&ctx, "zone \"%s\"", s); + + obj = cfg_tuple_get(zone_obj, "policy"); + if (!cfg_obj_isvoid(obj)) { + s = cfg_obj_asstring(cfg_tuple_get(obj, "policy name")); + conf_dnsrps_sadd(&ctx, " policy %s", s); + if (strcasecmp(s, "cname") == 0) { + s = cfg_obj_asstring( + cfg_tuple_get(obj, "cname")); + conf_dnsrps_sadd(&ctx, " %s", s); + } + } + + conf_dnsrps_yes_no(zone_obj, "recursive-only", &ctx); + conf_dnsrps_yes_no(zone_obj, "log", &ctx); + conf_dnsrps_num(zone_obj, "max-policy-ttl", &ctx); + obj = cfg_tuple_get(rpz_obj, "nsip-enable"); + if (!cfg_obj_isvoid(obj)) { + if (cfg_obj_asboolean(obj)) { + *nsip_on |= DNS_RPZ_ZBIT(rpz_num); + } else { + *nsip_on &= ~DNS_RPZ_ZBIT(rpz_num); + } + } + on = ((*nsip_on & DNS_RPZ_ZBIT(rpz_num)) != 0); + if (nsip_enabled != on) { + conf_dnsrps_sadd(&ctx, on ? " nsip-enable yes " + : " nsip-enable no "); + } + obj = cfg_tuple_get(rpz_obj, "nsdname-enable"); + if (!cfg_obj_isvoid(obj)) { + if (cfg_obj_asboolean(obj)) { + *nsdname_on |= DNS_RPZ_ZBIT(rpz_num); + } else { + *nsdname_on &= ~DNS_RPZ_ZBIT(rpz_num); + } + } + on = ((*nsdname_on & DNS_RPZ_ZBIT(rpz_num)) != 0); + if (nsdname_enabled != on) { + conf_dnsrps_sadd(&ctx, on ? " nsdname-enable yes " + : " nsdname-enable no "); + } + conf_dnsrps_sadd(&ctx, ";\n"); + zone_element = cfg_list_next(zone_element); + } + + conf_dnsrps_yes_no(rpz_obj, "recursive-only", &ctx); + conf_dnsrps_num(rpz_obj, "max-policy-ttl", &ctx); + conf_dnsrps_num(rpz_obj, "min-ns-dots", &ctx); + conf_dnsrps_yes_no(rpz_obj, "qname-wait-recurse", &ctx); + conf_dnsrps_yes_no(rpz_obj, "break-dnssec", &ctx); + if (!nsip_enabled) { + conf_dnsrps_sadd(&ctx, " nsip-enable no "); + } + if (!nsdname_enabled) { + conf_dnsrps_sadd(&ctx, " nsdname-enable no "); + } + + /* + * Get the general dnsrpzd parameters from the response-policy + * statement in the view and the general options. + */ + if (conf_dnsrps_get(&obj, maps, rpz_obj, "dnsrps-options", &ctx) && + obj != NULL) + { + conf_dnsrps_sadd(&ctx, " %s\n", cfg_obj_asstring(obj)); + } + + if (ctx.result == ISC_R_SUCCESS) { + *rps_cstr = ctx.cstr; + *rps_cstr_size = ctx.cstr_size; + } else { + if (ctx.cstr != NULL) { + isc_mem_put(ctx.mctx, ctx.cstr, ctx.cstr_size); + } + *rps_cstr = NULL; + *rps_cstr_size = 0; + } + return (ctx.result); +} +#endif /* ifdef USE_DNSRPS */ + +static isc_result_t +configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, + const char *str, const char *msg) { + isc_result_t result; + + result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "invalid %s '%s'", msg, str); + } + return (result); +} + +static isc_result_t +configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name, + const char *str, const dns_name_t *origin) { + isc_result_t result; + + result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE, + view->mctx); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "invalid zone '%s'", str); + } + return (result); +} + +static isc_result_t +configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element, + bool recursive_only_default, bool add_soa_default, + dns_ttl_t ttl_default, uint32_t minupdateinterval_default, + const dns_rpz_zone_t *old, bool *old_rpz_okp) { + const cfg_obj_t *rpz_obj, *obj; + const char *str; + dns_rpz_zone_t *zone = NULL; + isc_result_t result; + dns_rpz_num_t rpz_num; + + REQUIRE(old != NULL || !*old_rpz_okp); + + rpz_obj = cfg_listelt_value(element); + + if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES) { + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "limit of %d response policy zones exceeded", + DNS_RPZ_MAX_ZONES); + return (ISC_R_FAILURE); + } + + result = dns_rpz_new_zone(view->rpzs, &zone); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "Error creating new RPZ zone : %s", + isc_result_totext(result)); + return (result); + } + + obj = cfg_tuple_get(rpz_obj, "recursive-only"); + if (cfg_obj_isvoid(obj) ? recursive_only_default + : cfg_obj_asboolean(obj)) + { + view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(zone->num); + } else { + view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(zone->num); + } + + obj = cfg_tuple_get(rpz_obj, "log"); + if (!cfg_obj_isvoid(obj) && !cfg_obj_asboolean(obj)) { + view->rpzs->p.no_log |= DNS_RPZ_ZBIT(zone->num); + } else { + view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(zone->num); + } + + obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); + if (cfg_obj_isduration(obj)) { + zone->max_policy_ttl = cfg_obj_asduration(obj); + } else { + zone->max_policy_ttl = ttl_default; + } + if (*old_rpz_okp && zone->max_policy_ttl != old->max_policy_ttl) { + *old_rpz_okp = false; + } + + obj = cfg_tuple_get(rpz_obj, "min-update-interval"); + if (cfg_obj_isduration(obj)) { + zone->min_update_interval = cfg_obj_asduration(obj); + } else { + zone->min_update_interval = minupdateinterval_default; + } + if (*old_rpz_okp && + zone->min_update_interval != old->min_update_interval) + { + *old_rpz_okp = false; + } + + str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name")); + result = configure_rpz_name(view, rpz_obj, &zone->origin, str, "zone"); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (dns_name_equal(&zone->origin, dns_rootname)) { + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "invalid zone name '%s'", str); + return (DNS_R_EMPTYLABEL); + } + if (!view->rpzs->p.dnsrps_enabled) { + for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones - 1; + ++rpz_num) + { + if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, + &zone->origin)) + { + cfg_obj_log(rpz_obj, named_g_lctx, + DNS_RPZ_ERROR_LEVEL, + "duplicate '%s'", str); + result = DNS_R_DUPLICATE; + return (result); + } + } + } + if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin)) { + *old_rpz_okp = false; + } + + result = configure_rpz_name2(view, rpz_obj, &zone->client_ip, + DNS_RPZ_CLIENT_IP_ZONE, &zone->origin); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name2(view, rpz_obj, &zone->ip, DNS_RPZ_IP_ZONE, + &zone->origin); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name2(view, rpz_obj, &zone->nsdname, + DNS_RPZ_NSDNAME_ZONE, &zone->origin); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name2(view, rpz_obj, &zone->nsip, + DNS_RPZ_NSIP_ZONE, &zone->origin); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name(view, rpz_obj, &zone->passthru, + DNS_RPZ_PASSTHRU_NAME, "name"); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name(view, rpz_obj, &zone->drop, + DNS_RPZ_DROP_NAME, "name"); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = configure_rpz_name(view, rpz_obj, &zone->tcp_only, + DNS_RPZ_TCP_ONLY_NAME, "name"); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = cfg_tuple_get(rpz_obj, "policy"); + if (cfg_obj_isvoid(obj)) { + zone->policy = DNS_RPZ_POLICY_GIVEN; + } else { + str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name")); + zone->policy = dns_rpz_str2policy(str); + INSIST(zone->policy != DNS_RPZ_POLICY_ERROR); + if (zone->policy == DNS_RPZ_POLICY_CNAME) { + str = cfg_obj_asstring(cfg_tuple_get(obj, "cname")); + result = configure_rpz_name(view, rpz_obj, &zone->cname, + str, "cname"); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } + if (*old_rpz_okp && (zone->policy != old->policy || + !dns_name_equal(&old->cname, &zone->cname))) + { + *old_rpz_okp = false; + } + + obj = cfg_tuple_get(rpz_obj, "add-soa"); + if (cfg_obj_isvoid(obj)) { + zone->addsoa = add_soa_default; + } else { + zone->addsoa = cfg_obj_asboolean(obj); + } + if (*old_rpz_okp && zone->addsoa != old->addsoa) { + *old_rpz_okp = false; + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +configure_rpz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t **maps, + const cfg_obj_t *rpz_obj, bool *old_rpz_okp) { + bool dnsrps_enabled; + const cfg_listelt_t *zone_element; + char *rps_cstr; + size_t rps_cstr_size; + const cfg_obj_t *sub_obj; + bool recursive_only_default, add_soa_default; + bool nsip_enabled, nsdname_enabled; + dns_rpz_zbits_t nsip_on, nsdname_on; + dns_ttl_t ttl_default; + uint32_t minupdateinterval_default; + dns_rpz_zones_t *zones; + const dns_rpz_zones_t *old; + bool pview_must_detach = false; + const dns_rpz_zone_t *old_zone; + isc_result_t result; + int i; + + *old_rpz_okp = false; + + zone_element = cfg_list_first(cfg_tuple_get(rpz_obj, "zone list")); + if (zone_element == NULL) { + return (ISC_R_SUCCESS); + } + + nsip_enabled = true; + sub_obj = cfg_tuple_get(rpz_obj, "nsip-enable"); + if (!cfg_obj_isvoid(sub_obj)) { + nsip_enabled = cfg_obj_asboolean(sub_obj); + } + nsip_on = nsip_enabled ? DNS_RPZ_ALL_ZBITS : 0; + + nsdname_enabled = true; + sub_obj = cfg_tuple_get(rpz_obj, "nsdname-enable"); + if (!cfg_obj_isvoid(sub_obj)) { + nsdname_enabled = cfg_obj_asboolean(sub_obj); + } + nsdname_on = nsdname_enabled ? DNS_RPZ_ALL_ZBITS : 0; + + /* + * "dnsrps-enable yes|no" can be either a global or response-policy + * clause. + */ + dnsrps_enabled = false; + rps_cstr = NULL; + rps_cstr_size = 0; + sub_obj = NULL; + (void)named_config_get(maps, "dnsrps-enable", &sub_obj); + if (sub_obj != NULL) { + dnsrps_enabled = cfg_obj_asboolean(sub_obj); + } + sub_obj = cfg_tuple_get(rpz_obj, "dnsrps-enable"); + if (!cfg_obj_isvoid(sub_obj)) { + dnsrps_enabled = cfg_obj_asboolean(sub_obj); + } +#ifndef USE_DNSRPS + if (dnsrps_enabled) { + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "\"dnsrps-enable yes\" but" + " without `./configure --enable-dnsrps`"); + return (ISC_R_FAILURE); + } +#else /* ifndef USE_DNSRPS */ + if (dnsrps_enabled) { + if (librpz == NULL) { + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL, + "\"dnsrps-enable yes\" but %s", + librpz_lib_open_emsg.c); + return (ISC_R_FAILURE); + } + + /* + * Generate the DNS Response Policy Service + * configuration string. + */ + result = conf_dnsrps(view, maps, nsip_enabled, nsdname_enabled, + &nsip_on, &nsdname_on, &rps_cstr, + &rps_cstr_size, rpz_obj, zone_element); + if (result != ISC_R_SUCCESS) { + return (result); + } + } +#endif /* ifndef USE_DNSRPS */ + + result = dns_rpz_new_zones(view->mctx, named_g_taskmgr, + named_g_timermgr, rps_cstr, rps_cstr_size, + &view->rpzs); + if (result != ISC_R_SUCCESS) { + return (result); + } + + zones = view->rpzs; + + zones->p.nsip_on = nsip_on; + zones->p.nsdname_on = nsdname_on; + + sub_obj = cfg_tuple_get(rpz_obj, "recursive-only"); + if (!cfg_obj_isvoid(sub_obj) && !cfg_obj_asboolean(sub_obj)) { + recursive_only_default = false; + } else { + recursive_only_default = true; + } + + sub_obj = cfg_tuple_get(rpz_obj, "add-soa"); + if (!cfg_obj_isvoid(sub_obj) && !cfg_obj_asboolean(sub_obj)) { + add_soa_default = false; + } else { + add_soa_default = true; + } + + sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec"); + if (!cfg_obj_isvoid(sub_obj) && cfg_obj_asboolean(sub_obj)) { + zones->p.break_dnssec = true; + } else { + zones->p.break_dnssec = false; + } + + sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); + if (cfg_obj_isduration(sub_obj)) { + ttl_default = cfg_obj_asduration(sub_obj); + } else { + ttl_default = DNS_RPZ_MAX_TTL_DEFAULT; + } + + sub_obj = cfg_tuple_get(rpz_obj, "min-update-interval"); + if (cfg_obj_isduration(sub_obj)) { + minupdateinterval_default = cfg_obj_asduration(sub_obj); + } else { + minupdateinterval_default = DNS_RPZ_MINUPDATEINTERVAL_DEFAULT; + } + + sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots"); + if (cfg_obj_isuint32(sub_obj)) { + zones->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1; + } else { + zones->p.min_ns_labels = 2; + } + + sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse"); + if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) { + zones->p.qname_wait_recurse = true; + } else { + zones->p.qname_wait_recurse = false; + } + + sub_obj = cfg_tuple_get(rpz_obj, "nsdname-wait-recurse"); + if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) { + zones->p.nsdname_wait_recurse = true; + } else { + zones->p.nsdname_wait_recurse = false; + } + + sub_obj = cfg_tuple_get(rpz_obj, "nsip-wait-recurse"); + if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj)) { + zones->p.nsip_wait_recurse = true; + } else { + zones->p.nsip_wait_recurse = false; + } + + if (pview != NULL) { + old = pview->rpzs; + } else { + result = dns_viewlist_find(&named_g_server->viewlist, + view->name, view->rdclass, &pview); + if (result == ISC_R_SUCCESS) { + pview_must_detach = true; + old = pview->rpzs; + } else { + old = NULL; + } + } + + if (old == NULL) { + *old_rpz_okp = false; + } else { + *old_rpz_okp = true; + } + + for (i = 0; zone_element != NULL; + ++i, zone_element = cfg_list_next(zone_element)) + { + INSIST(!*old_rpz_okp || old != NULL); + if (*old_rpz_okp && i < old->p.num_zones) { + old_zone = old->zones[i]; + } else { + *old_rpz_okp = false; + old_zone = NULL; + } + result = configure_rpz_zone( + view, zone_element, recursive_only_default, + add_soa_default, ttl_default, minupdateinterval_default, + old_zone, old_rpz_okp); + if (result != ISC_R_SUCCESS) { + if (pview_must_detach) { + dns_view_detach(&pview); + } + return (result); + } + } + + /* + * If this is a reloading and the parameters and list of policy + * zones are unchanged, then use the same policy data. + * Data for individual zones that must be reloaded will be merged. + */ + if (*old_rpz_okp) { + if (old != NULL && + memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0) + { + *old_rpz_okp = false; + } else if ((old == NULL || old->rps_cstr == NULL) != + (zones->rps_cstr == NULL)) + { + *old_rpz_okp = false; + } else if (old != NULL && zones->rps_cstr != NULL && + strcmp(old->rps_cstr, zones->rps_cstr) != 0) + { + *old_rpz_okp = false; + } + } + + if (*old_rpz_okp) { + dns_rpz_shutdown_rpzs(view->rpzs); + dns_rpz_detach_rpzs(&view->rpzs); + dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs); + dns_rpz_detach_rpzs(&pview->rpzs); + } else if (old != NULL && pview != NULL) { + ++pview->rpzs->rpz_ver; + view->rpzs->rpz_ver = pview->rpzs->rpz_ver; + cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_DEBUG_LEVEL1, + "updated RPZ policy: version %d", + view->rpzs->rpz_ver); + } + + if (pview_must_detach) { + dns_view_detach(&pview); + } + + return (ISC_R_SUCCESS); +} + +static void +catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) { + catz_chgzone_event_t *ev = (catz_chgzone_event_t *)event0; + isc_result_t result; + dns_forwarders_t *dnsforwarders = NULL; + dns_name_t *name = NULL; + isc_buffer_t namebuf; + isc_buffer_t *confbuf; + char nameb[DNS_NAME_FORMATSIZE]; + const cfg_obj_t *zlist = NULL; + cfg_obj_t *zoneconf = NULL; + cfg_obj_t *zoneobj = NULL; + ns_cfgctx_t *cfg; + dns_zone_t *zone = NULL; + + cfg = (ns_cfgctx_t *)ev->view->new_zone_config; + if (cfg == NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "catz: allow-new-zones statement missing from " + "config; cannot add zone from the catalog"); + goto cleanup; + } + + name = dns_catz_entry_getname(ev->entry); + + isc_buffer_init(&namebuf, nameb, DNS_NAME_FORMATSIZE); + dns_name_totext(name, true, &namebuf); + isc_buffer_putuint8(&namebuf, 0); + + result = dns_fwdtable_find(ev->view->fwdtable, name, NULL, + &dnsforwarders); + if (result == ISC_R_SUCCESS && + dnsforwarders->fwdpolicy == dns_fwdpolicy_only) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_addmodzone_taskaction: " + "zone '%s' will not be processed because of the " + "explicitly configured forwarding for that zone", + nameb); + goto cleanup; + } + + result = dns_zt_find(ev->view->zonetable, name, 0, NULL, &zone); + + if (ev->mod) { + dns_catz_zone_t *parentcatz; + + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: error \"%s\" while trying to " + "modify zone '%s'", + isc_result_totext(result), nameb); + goto cleanup; + } + + if (!dns_zone_getadded(zone)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_addmodzone_taskaction: " + "zone '%s' is not a dynamically " + "added zone", + nameb); + goto cleanup; + } + + parentcatz = dns_zone_get_parentcatz(zone); + + if (parentcatz == NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_addmodzone_taskaction: " + "zone '%s' exists and is not added by " + "a catalog zone, so won't be modified", + nameb); + goto cleanup; + } + if (parentcatz != ev->origin) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_addmodzone_taskaction: " + "zone '%s' exists in multiple " + "catalog zones", + nameb); + goto cleanup; + } + + dns_zone_detach(&zone); + } else { + /* Zone shouldn't already exist when adding */ + if (result == ISC_R_SUCCESS) { + if (dns_zone_get_parentcatz(zone) == NULL) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: " + "catz_addmodzone_taskaction: " + "zone '%s' will not be added " + "because it is an explicitly " + "configured zone", + nameb); + } else { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: " + "catz_addmodzone_taskaction: " + "zone '%s' will not be added " + "because another catalog zone " + "already contains an entry with " + "that zone", + nameb); + } + goto cleanup; + } else if (result != ISC_R_NOTFOUND && + result != DNS_R_PARTIALMATCH) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: error \"%s\" while trying to " + "add zone '%s'", + isc_result_totext(result), nameb); + goto cleanup; + } else { /* this can happen in case of DNS_R_PARTIALMATCH */ + if (zone != NULL) { + dns_zone_detach(&zone); + } + } + } + RUNTIME_CHECK(zone == NULL); + /* Create a config for new zone */ + confbuf = NULL; + result = dns_catz_generate_zonecfg(ev->origin, ev->entry, &confbuf); + if (result == ISC_R_SUCCESS) { + cfg_parser_reset(cfg->add_parser); + result = cfg_parse_buffer(cfg->add_parser, confbuf, "catz", 0, + &cfg_type_addzoneconf, 0, &zoneconf); + isc_buffer_free(&confbuf); + } + /* + * Fail if either dns_catz_generate_zonecfg() or cfg_parse_buffer3() + * failed. + */ + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "catz: error \"%s\" while trying to generate " + "config for zone '%s'", + isc_result_totext(result), nameb); + goto cleanup; + } + CHECK(cfg_map_get(zoneconf, "zone", &zlist)); + if (!cfg_obj_islist(zlist)) { + CHECK(ISC_R_FAILURE); + } + + /* For now we only support adding one zone at a time */ + zoneobj = cfg_listelt_value(cfg_list_first(zlist)); + + /* Mark view unfrozen so that zone can be added */ + + result = isc_task_beginexclusive(task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_view_thaw(ev->view); + result = configure_zone( + cfg->config, zoneobj, cfg->vconfig, ev->cbd->server->mctx, + ev->view, &ev->cbd->server->viewlist, + &ev->cbd->server->kasplist, cfg->actx, true, false, ev->mod); + dns_view_freeze(ev->view); + isc_task_endexclusive(task); + + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: failed to configure zone '%s' - %d", nameb, + result); + goto cleanup; + } + + /* Is it there yet? */ + CHECK(dns_zt_find(ev->view->zonetable, name, 0, NULL, &zone)); + + /* + * Load the zone from the master file. If this fails, we'll + * need to undo the configuration we've done already. + */ + result = dns_zone_load(zone, true); + if (result != ISC_R_SUCCESS) { + dns_db_t *dbp = NULL; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "catz: dns_zone_load() failed " + "with %s; reverting.", + isc_result_totext(result)); + + /* If the zone loaded partially, unload it */ + if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(zone); + } + + /* Remove the zone from the zone table */ + dns_zt_unmount(ev->view->zonetable, zone); + goto cleanup; + } + + /* Flag the zone as having been added at runtime */ + dns_zone_setadded(zone, true); + dns_zone_set_parentcatz(zone, ev->origin); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + if (zoneconf != NULL) { + cfg_obj_destroy(cfg->add_parser, &zoneconf); + } + dns_catz_entry_detach(ev->origin, &ev->entry); + dns_catz_detach_catz(&ev->origin); + dns_view_detach(&ev->view); + isc_event_free(ISC_EVENT_PTR(&ev)); +} + +static void +catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) { + catz_chgzone_event_t *ev = (catz_chgzone_event_t *)event0; + isc_result_t result; + dns_zone_t *zone = NULL; + dns_db_t *dbp = NULL; + char cname[DNS_NAME_FORMATSIZE]; + const char *file; + + result = isc_task_beginexclusive(task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_name_format(dns_catz_entry_getname(ev->entry), cname, + DNS_NAME_FORMATSIZE); + result = dns_zt_find(ev->view->zonetable, + dns_catz_entry_getname(ev->entry), 0, NULL, &zone); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_delzone_taskaction: " + "zone '%s' not found", + cname); + goto cleanup; + } + + if (!dns_zone_getadded(zone)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_delzone_taskaction: " + "zone '%s' is not a dynamically added zone", + cname); + goto cleanup; + } + + if (dns_zone_get_parentcatz(zone) != ev->origin) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_delzone_taskaction: zone " + "'%s' exists in multiple catalog zones", + cname); + goto cleanup; + } + + /* Stop answering for this zone */ + if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(zone); + } + + CHECK(dns_zt_unmount(ev->view->zonetable, zone)); + file = dns_zone_getfile(zone); + if (file != NULL) { + isc_file_remove(file); + file = dns_zone_getjournal(zone); + if (file != NULL) { + isc_file_remove(file); + } + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "catz: catz_delzone_taskaction: " + "zone '%s' deleted", + cname); +cleanup: + isc_task_endexclusive(task); + if (zone != NULL) { + dns_zone_detach(&zone); + } + dns_catz_entry_detach(ev->origin, &ev->entry); + dns_catz_detach_catz(&ev->origin); + dns_view_detach(&ev->view); + isc_event_free(ISC_EVENT_PTR(&ev)); +} + +static isc_result_t +catz_create_chg_task(dns_catz_entry_t *entry, dns_catz_zone_t *origin, + dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata, + isc_eventtype_t type) { + catz_chgzone_event_t *event = NULL; + isc_task_t *task = NULL; + isc_result_t result; + isc_taskaction_t action = NULL; + + result = isc_taskmgr_excltask(taskmgr, &task); + if (result != ISC_R_SUCCESS) { + return (result); + } + + switch (type) { + case DNS_EVENT_CATZADDZONE: + case DNS_EVENT_CATZMODZONE: + action = catz_addmodzone_taskaction; + break; + case DNS_EVENT_CATZDELZONE: + action = catz_delzone_taskaction; + break; + default: + REQUIRE(0); + UNREACHABLE(); + } + + event = (catz_chgzone_event_t *)isc_event_allocate( + view->mctx, origin, type, action, NULL, sizeof(*event)); + + event->cbd = (catz_cb_data_t *)udata; + event->entry = NULL; + event->origin = NULL; + event->view = NULL; + event->mod = (type == DNS_EVENT_CATZMODZONE); + + dns_catz_entry_attach(entry, &event->entry); + dns_catz_attach_catz(origin, &event->origin); + dns_view_attach(view, &event->view); + + isc_task_send(task, ISC_EVENT_PTR(&event)); + isc_task_detach(&task); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +catz_addzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view, + isc_taskmgr_t *taskmgr, void *udata) { + return (catz_create_chg_task(entry, origin, view, taskmgr, udata, + DNS_EVENT_CATZADDZONE)); +} + +static isc_result_t +catz_delzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view, + isc_taskmgr_t *taskmgr, void *udata) { + return (catz_create_chg_task(entry, origin, view, taskmgr, udata, + DNS_EVENT_CATZDELZONE)); +} + +static isc_result_t +catz_modzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin, dns_view_t *view, + isc_taskmgr_t *taskmgr, void *udata) { + return (catz_create_chg_task(entry, origin, view, taskmgr, udata, + DNS_EVENT_CATZMODZONE)); +} + +static isc_result_t +configure_catz_zone(dns_view_t *view, dns_view_t *pview, + const cfg_obj_t *config, const cfg_listelt_t *element) { + const cfg_obj_t *catz_obj, *obj; + dns_catz_zone_t *zone = NULL; + const char *str; + isc_result_t result; + dns_name_t origin; + dns_catz_options_t *opts; + + dns_name_init(&origin, NULL); + catz_obj = cfg_listelt_value(element); + + str = cfg_obj_asstring(cfg_tuple_get(catz_obj, "zone name")); + + result = dns_name_fromstring(&origin, str, DNS_NAME_DOWNCASE, + view->mctx); + if (result == ISC_R_SUCCESS && dns_name_equal(&origin, dns_rootname)) { + result = DNS_R_EMPTYLABEL; + } + + if (result != ISC_R_SUCCESS) { + cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL, + "catz: invalid zone name '%s'", str); + goto cleanup; + } + + result = dns_catz_add_zone(view->catzs, &origin, &zone); + if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) { + cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL, + "catz: unable to create catalog zone '%s', " + "error %s", + str, isc_result_totext(result)); + goto cleanup; + } + + if (result == ISC_R_EXISTS) { + isc_ht_iter_t *it = NULL; + + RUNTIME_CHECK(pview != NULL); + + /* + * xxxwpk todo: reconfigure the zone!!!! + */ + cfg_obj_log(catz_obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL, + "catz: catalog zone '%s' will not be reconfigured", + str); + /* + * We have to walk through all the member zones and attach + * them to current view + */ + dns_catz_get_iterator(zone, &it); + + for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; + result = isc_ht_iter_next(it)) + { + dns_name_t *name = NULL; + dns_zone_t *dnszone = NULL; + dns_catz_entry_t *entry = NULL; + isc_result_t tresult; + + isc_ht_iter_current(it, (void **)&entry); + name = dns_catz_entry_getname(entry); + + tresult = dns_view_findzone(pview, name, &dnszone); + if (tresult != ISC_R_SUCCESS) { + continue; + } + + dns_zone_setview(dnszone, view); + dns_view_addzone(view, dnszone); + + /* + * The dns_view_findzone() call above increments the + * zone's reference count, which we need to decrement + * back. However, as dns_zone_detach() sets the + * supplied pointer to NULL, calling it is deferred + * until the dnszone variable is no longer used. + */ + dns_zone_detach(&dnszone); + } + + isc_ht_iter_destroy(&it); + + result = ISC_R_SUCCESS; + } + + dns_catz_zone_resetdefoptions(zone); + opts = dns_catz_zone_getdefoptions(zone); + + obj = cfg_tuple_get(catz_obj, "default-masters"); + if (obj == NULL || !cfg_obj_istuple(obj)) { + obj = cfg_tuple_get(catz_obj, "default-primaries"); + } + if (obj != NULL && cfg_obj_istuple(obj)) { + result = named_config_getipandkeylist( + config, "primaries", obj, view->mctx, &opts->masters); + } + + obj = cfg_tuple_get(catz_obj, "in-memory"); + if (obj != NULL && cfg_obj_isboolean(obj)) { + opts->in_memory = cfg_obj_asboolean(obj); + } + + obj = cfg_tuple_get(catz_obj, "zone-directory"); + if (!opts->in_memory && obj != NULL && cfg_obj_isstring(obj)) { + opts->zonedir = isc_mem_strdup(view->mctx, + cfg_obj_asstring(obj)); + if (isc_file_isdirectory(opts->zonedir) != ISC_R_SUCCESS) { + cfg_obj_log(obj, named_g_lctx, DNS_CATZ_ERROR_LEVEL, + "catz: zone-directory '%s' " + "not found; zone files will not be " + "saved", + opts->zonedir); + opts->in_memory = true; + } + } + + obj = cfg_tuple_get(catz_obj, "min-update-interval"); + if (obj != NULL && cfg_obj_isduration(obj)) { + opts->min_update_interval = cfg_obj_asduration(obj); + } + +cleanup: + dns_name_free(&origin, view->mctx); + + return (result); +} + +static catz_cb_data_t ns_catz_cbdata; +static dns_catz_zonemodmethods_t ns_catz_zonemodmethods = { + catz_addzone, catz_modzone, catz_delzone, &ns_catz_cbdata +}; + +static isc_result_t +configure_catz(dns_view_t *view, dns_view_t *pview, const cfg_obj_t *config, + const cfg_obj_t *catz_obj) { + const cfg_listelt_t *zone_element = NULL; + const dns_catz_zones_t *old = NULL; + bool pview_must_detach = false; + isc_result_t result; + + /* xxxwpk TODO do it cleaner, once, somewhere */ + ns_catz_cbdata.server = named_g_server; + + zone_element = cfg_list_first(cfg_tuple_get(catz_obj, "zone list")); + if (zone_element == NULL) { + return (ISC_R_SUCCESS); + } + + CHECK(dns_catz_new_zones(view->mctx, named_g_taskmgr, named_g_timermgr, + &view->catzs, &ns_catz_zonemodmethods)); + + if (pview != NULL) { + old = pview->catzs; + } else { + result = dns_viewlist_find(&named_g_server->viewlist, + view->name, view->rdclass, &pview); + if (result == ISC_R_SUCCESS) { + pview_must_detach = true; + old = pview->catzs; + } + } + + if (old != NULL) { + dns_catz_shutdown_catzs(view->catzs); + dns_catz_detach_catzs(&view->catzs); + dns_catz_attach_catzs(pview->catzs, &view->catzs); + dns_catz_detach_catzs(&pview->catzs); + dns_catz_prereconfig(view->catzs); + } + + while (zone_element != NULL) { + CHECK(configure_catz_zone(view, pview, config, zone_element)); + zone_element = cfg_list_next(zone_element); + } + + if (old != NULL) { + dns_catz_postreconfig(view->catzs); + } + + result = ISC_R_SUCCESS; + +cleanup: + if (pview_must_detach) { + dns_view_detach(&pview); + } + + return (result); +} + +#define CHECK_RRL(cond, pat, val1, val2) \ + do { \ + if (!(cond)) { \ + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, pat, \ + val1, val2); \ + result = ISC_R_RANGE; \ + goto cleanup; \ + } \ + } while (0) + +#define CHECK_RRL_RATE(rate, def, max_rate, name) \ + do { \ + obj = NULL; \ + rrl->rate.str = name; \ + result = cfg_map_get(map, name, &obj); \ + if (result == ISC_R_SUCCESS) { \ + rrl->rate.r = cfg_obj_asuint32(obj); \ + CHECK_RRL(rrl->rate.r <= max_rate, name " %d > %d", \ + rrl->rate.r, max_rate); \ + } else { \ + rrl->rate.r = def; \ + } \ + rrl->rate.scaled = rrl->rate.r; \ + } while (0) + +static isc_result_t +configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) { + const cfg_obj_t *obj; + dns_rrl_t *rrl; + isc_result_t result; + int min_entries, i, j; + + /* + * Most DNS servers have few clients, but intentinally open + * recursive and authoritative servers often have many. + * So start with a small number of entries unless told otherwise + * to reduce cold-start costs. + */ + min_entries = 500; + obj = NULL; + result = cfg_map_get(map, "min-table-size", &obj); + if (result == ISC_R_SUCCESS) { + min_entries = cfg_obj_asuint32(obj); + if (min_entries < 1) { + min_entries = 1; + } + } + result = dns_rrl_init(&rrl, view, min_entries); + if (result != ISC_R_SUCCESS) { + return (result); + } + + i = ISC_MAX(20000, min_entries); + obj = NULL; + result = cfg_map_get(map, "max-table-size", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= min_entries, + "max-table-size %d < min-table-size %d", i, + min_entries); + } + rrl->max_entries = i; + + CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE, + "responses-per-second"); + CHECK_RRL_RATE(referrals_per_second, rrl->responses_per_second.r, + DNS_RRL_MAX_RATE, "referrals-per-second"); + CHECK_RRL_RATE(nodata_per_second, rrl->responses_per_second.r, + DNS_RRL_MAX_RATE, "nodata-per-second"); + CHECK_RRL_RATE(nxdomains_per_second, rrl->responses_per_second.r, + DNS_RRL_MAX_RATE, "nxdomains-per-second"); + CHECK_RRL_RATE(errors_per_second, rrl->responses_per_second.r, + DNS_RRL_MAX_RATE, "errors-per-second"); + + CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE, "all-per-second"); + + CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP, "slip"); + + i = 15; + obj = NULL; + result = cfg_map_get(map, "window", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW, + "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW); + } + rrl->window = i; + + i = 0; + obj = NULL; + result = cfg_map_get(map, "qps-scale", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, ""); + } + rrl->qps_scale = i; + rrl->qps = 1.0; + + i = 24; + obj = NULL; + result = cfg_map_get(map, "ipv4-prefix-length", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 8 && i <= 32, + "invalid 'ipv4-prefix-length %d'%s", i, ""); + } + rrl->ipv4_prefixlen = i; + if (i == 32) { + rrl->ipv4_mask = 0xffffffff; + } else { + rrl->ipv4_mask = htonl(0xffffffff << (32 - i)); + } + + i = 56; + obj = NULL; + result = cfg_map_get(map, "ipv6-prefix-length", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX, + "ipv6-prefix-length %d < 16 or > %d", i, + DNS_RRL_MAX_PREFIX); + } + rrl->ipv6_prefixlen = i; + for (j = 0; j < 4; ++j) { + if (i <= 0) { + rrl->ipv6_mask[j] = 0; + } else if (i < 32) { + rrl->ipv6_mask[j] = htonl(0xffffffff << (32 - i)); + } else { + rrl->ipv6_mask[j] = 0xffffffff; + } + i -= 32; + } + + obj = NULL; + result = cfg_map_get(map, "exempt-clients", &obj); + if (result == ISC_R_SUCCESS) { + result = cfg_acl_fromconfig(obj, config, named_g_lctx, + named_g_aclconfctx, named_g_mctx, 0, + &rrl->exempt); + CHECK_RRL(result == ISC_R_SUCCESS, "invalid %s%s", + "address match list", ""); + } + + obj = NULL; + result = cfg_map_get(map, "log-only", &obj); + if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj)) { + rrl->log_only = true; + } else { + rrl->log_only = false; + } + + return (ISC_R_SUCCESS); + +cleanup: + dns_rrl_view_destroy(view); + return (result); +} + +static isc_result_t +add_soa(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name, + const dns_name_t *origin, const dns_name_t *contact) { + dns_dbnode_t *node = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + isc_result_t result; + unsigned char buf[DNS_SOA_BUFFERSIZE]; + + CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db), 0, 28800, + 7200, 604800, 86400, buf, &rdata)); + + dns_rdatalist_init(&rdatalist); + rdatalist.type = rdata.type; + rdatalist.rdclass = rdata.rdclass; + rdatalist.ttl = 86400; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + + dns_rdataset_init(&rdataset); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_db_findnode(db, name, true, &node)); + CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); + +cleanup: + if (node != NULL) { + dns_db_detachnode(db, &node); + } + return (result); +} + +static isc_result_t +add_ns(dns_db_t *db, dns_dbversion_t *version, const dns_name_t *name, + const dns_name_t *nsname) { + dns_dbnode_t *node = NULL; + dns_rdata_ns_t ns; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatalist_t rdatalist; + dns_rdataset_t rdataset; + isc_result_t result; + isc_buffer_t b; + unsigned char buf[DNS_NAME_MAXWIRE]; + + isc_buffer_init(&b, buf, sizeof(buf)); + + ns.common.rdtype = dns_rdatatype_ns; + ns.common.rdclass = dns_db_class(db); + ns.mctx = NULL; + dns_name_init(&ns.name, NULL); + dns_name_clone(nsname, &ns.name); + CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns, + &ns, &b)); + + dns_rdatalist_init(&rdatalist); + rdatalist.type = rdata.type; + rdatalist.rdclass = rdata.rdclass; + rdatalist.ttl = 86400; + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + + dns_rdataset_init(&rdataset); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_db_findnode(db, name, true, &node)); + CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL)); + +cleanup: + if (node != NULL) { + dns_db_detachnode(db, &node); + } + return (result); +} + +static isc_result_t +create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, + const cfg_obj_t *zonelist, const char **empty_dbtype, + int empty_dbtypec, dns_zonestat_level_t statlevel) { + char namebuf[DNS_NAME_FORMATSIZE]; + const cfg_listelt_t *element; + const cfg_obj_t *obj; + const cfg_obj_t *zconfig; + const cfg_obj_t *zoptions; + const char *rbt_dbtype[4] = { "rbt" }; + const char *sep = ": view "; + const char *str; + const char *viewname = view->name; + dns_db_t *db = NULL; + dns_dbversion_t *version = NULL; + dns_fixedname_t cfixed; + dns_fixedname_t fixed; + dns_fixedname_t nsfixed; + dns_name_t *contact; + dns_name_t *ns; + dns_name_t *zname; + dns_zone_t *zone = NULL; + int rbt_dbtypec = 1; + isc_result_t result; + dns_namereln_t namereln; + int order; + unsigned int nlabels; + + zname = dns_fixedname_initname(&fixed); + ns = dns_fixedname_initname(&nsfixed); + contact = dns_fixedname_initname(&cfixed); + + /* + * Look for forward "zones" beneath this empty zone and if so + * create a custom db for the empty zone. + */ + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + zconfig = cfg_listelt_value(element); + str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + CHECK(dns_name_fromstring(zname, str, 0, NULL)); + namereln = dns_name_fullcompare(zname, name, &order, &nlabels); + if (namereln != dns_namereln_subdomain) { + continue; + } + + zoptions = cfg_tuple_get(zconfig, "options"); + + obj = NULL; + (void)cfg_map_get(zoptions, "type", &obj); + if (obj != NULL && + strcasecmp(cfg_obj_asstring(obj), "forward") == 0) + { + obj = NULL; + (void)cfg_map_get(zoptions, "forward", &obj); + if (obj == NULL) { + continue; + } + if (strcasecmp(cfg_obj_asstring(obj), "only") != 0) { + continue; + } + } + if (db == NULL) { + CHECK(dns_db_create(view->mctx, "rbt", name, + dns_dbtype_zone, view->rdclass, 0, + NULL, &db)); + CHECK(dns_db_newversion(db, &version)); + if (strcmp(empty_dbtype[2], "@") == 0) { + dns_name_clone(name, ns); + } else { + CHECK(dns_name_fromstring(ns, empty_dbtype[2], + 0, NULL)); + } + CHECK(dns_name_fromstring(contact, empty_dbtype[3], 0, + NULL)); + CHECK(add_soa(db, version, name, ns, contact)); + CHECK(add_ns(db, version, name, ns)); + } + CHECK(add_ns(db, version, zname, dns_rootname)); + } + + /* + * Is the existing zone the ok to use? + */ + if (pzone != NULL) { + unsigned int typec; + const char **dbargv; + + if (db != NULL) { + typec = rbt_dbtypec; + dbargv = rbt_dbtype; + } else { + typec = empty_dbtypec; + dbargv = empty_dbtype; + } + + result = check_dbtype(pzone, typec, dbargv, view->mctx); + if (result != ISC_R_SUCCESS) { + pzone = NULL; + } + + if (pzone != NULL && + dns_zone_gettype(pzone) != dns_zone_primary) + { + pzone = NULL; + } + if (pzone != NULL && dns_zone_getfile(pzone) != NULL) { + pzone = NULL; + } + if (pzone != NULL) { + dns_zone_getraw(pzone, &zone); + if (zone != NULL) { + dns_zone_detach(&zone); + pzone = NULL; + } + } + } + + if (pzone == NULL) { + CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone)); + CHECK(dns_zone_setorigin(zone, name)); + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone)); + if (db == NULL) { + dns_zone_setdbtype(zone, empty_dbtypec, empty_dbtype); + } + dns_zone_setclass(zone, view->rdclass); + dns_zone_settype(zone, dns_zone_primary); + dns_zone_setstats(zone, named_g_server->zonestats); + } else { + dns_zone_attach(pzone, &zone); + } + + dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, false); + dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); + dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setdialup(zone, dns_dialuptype_no); + dns_zone_setautomatic(zone, true); + if (view->queryacl != NULL) { + dns_zone_setqueryacl(zone, view->queryacl); + } else { + dns_zone_clearqueryacl(zone); + } + if (view->queryonacl != NULL) { + dns_zone_setqueryonacl(zone, view->queryonacl); + } else { + dns_zone_clearqueryonacl(zone); + } + dns_zone_clearupdateacl(zone); + if (view->transferacl != NULL) { + dns_zone_setxfracl(zone, view->transferacl); + } else { + dns_zone_clearxfracl(zone); + } + + CHECK(setquerystats(zone, view->mctx, statlevel)); + if (db != NULL) { + dns_db_closeversion(db, &version, true); + CHECK(dns_zone_replacedb(zone, db, false)); + } + dns_zone_setoption(zone, DNS_ZONEOPT_AUTOEMPTY, true); + dns_zone_setview(zone, view); + CHECK(dns_view_addzone(view, zone)); + + if (!strcmp(viewname, "_default")) { + sep = ""; + viewname = ""; + } + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_ZONELOAD, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "automatic empty zone%s%s: %s", sep, viewname, namebuf); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + if (version != NULL) { + dns_db_closeversion(db, &version, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + + INSIST(version == NULL); + + return (result); +} + +static isc_result_t +create_ipv4only_zone(dns_zone_t *pzone, dns_view_t *view, + const dns_name_t *name, const char *type, isc_mem_t *mctx, + const char *server, const char *contact) { + char namebuf[DNS_NAME_FORMATSIZE]; + const char *dbtype[4] = { "_builtin", NULL, "@", "." }; + const char *sep = ": view "; + const char *viewname = view->name; + dns_zone_t *zone = NULL; + int dbtypec = 4; + isc_result_t result; + + REQUIRE(type != NULL); + + if (!strcmp(viewname, "_default")) { + sep = ""; + viewname = ""; + } + + dbtype[1] = type; + if (server != NULL) { + dbtype[2] = server; + } + if (contact != NULL) { + dbtype[3] = contact; + } + + if (pzone != NULL) { + result = check_dbtype(pzone, dbtypec, dbtype, view->mctx); + if (result != ISC_R_SUCCESS) { + pzone = NULL; + } + } + + if (pzone == NULL) { + /* + * Create the actual zone. + */ + CHECK(dns_zone_create(&zone, mctx)); + CHECK(dns_zone_setorigin(zone, name)); + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone)); + dns_zone_setclass(zone, view->rdclass); + dns_zone_settype(zone, dns_zone_primary); + dns_zone_setstats(zone, named_g_server->zonestats); + dns_zone_setdbtype(zone, dbtypec, dbtype); + dns_zone_setdialup(zone, dns_dialuptype_no); + dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setautomatic(zone, true); + dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); + } else { + dns_zone_attach(pzone, &zone); + } + if (view->queryacl != NULL) { + dns_zone_setqueryacl(zone, view->queryacl); + } else { + dns_zone_clearqueryacl(zone); + } + if (view->queryonacl != NULL) { + dns_zone_setqueryonacl(zone, view->queryonacl); + } else { + dns_zone_clearqueryonacl(zone); + } + dns_zone_setview(zone, view); + CHECK(dns_view_addzone(view, zone)); + + dns_name_format(name, namebuf, sizeof(namebuf)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "automatic ipv4only zone%s%s: %s", sep, viewname, + namebuf); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + return (result); +} + +#ifdef HAVE_DNSTAP +static isc_result_t +configure_dnstap(const cfg_obj_t **maps, dns_view_t *view) { + isc_result_t result; + const cfg_obj_t *obj, *obj2; + const cfg_listelt_t *element; + const char *dpath; + const cfg_obj_t *dlist = NULL; + dns_dtmsgtype_t dttypes = 0; + unsigned int i; + struct fstrm_iothr_options *fopt = NULL; + + result = named_config_get(maps, "dnstap", &dlist); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + for (element = cfg_list_first(dlist); element != NULL; + element = cfg_list_next(element)) + { + const char *str; + dns_dtmsgtype_t dt = 0; + + obj = cfg_listelt_value(element); + obj2 = cfg_tuple_get(obj, "type"); + str = cfg_obj_asstring(obj2); + if (strcasecmp(str, "client") == 0) { + dt |= DNS_DTTYPE_CQ | DNS_DTTYPE_CR; + } else if (strcasecmp(str, "auth") == 0) { + dt |= DNS_DTTYPE_AQ | DNS_DTTYPE_AR; + } else if (strcasecmp(str, "resolver") == 0) { + dt |= DNS_DTTYPE_RQ | DNS_DTTYPE_RR; + } else if (strcasecmp(str, "forwarder") == 0) { + dt |= DNS_DTTYPE_FQ | DNS_DTTYPE_FR; + } else if (strcasecmp(str, "update") == 0) { + dt |= DNS_DTTYPE_UQ | DNS_DTTYPE_UR; + } else if (strcasecmp(str, "all") == 0) { + dt |= DNS_DTTYPE_CQ | DNS_DTTYPE_CR | DNS_DTTYPE_AQ | + DNS_DTTYPE_AR | DNS_DTTYPE_RQ | DNS_DTTYPE_RR | + DNS_DTTYPE_FQ | DNS_DTTYPE_FR | DNS_DTTYPE_UQ | + DNS_DTTYPE_UR; + } + + obj2 = cfg_tuple_get(obj, "mode"); + if (obj2 == NULL || cfg_obj_isvoid(obj2)) { + dttypes |= dt; + continue; + } + + str = cfg_obj_asstring(obj2); + if (strcasecmp(str, "query") == 0) { + dt &= ~DNS_DTTYPE_RESPONSE; + } else if (strcasecmp(str, "response") == 0) { + dt &= ~DNS_DTTYPE_QUERY; + } + + dttypes |= dt; + } + + if (named_g_server->dtenv == NULL && dttypes != 0) { + dns_dtmode_t dmode; + uint64_t max_size = 0; + uint32_t rolls = 0; + isc_log_rollsuffix_t suffix = isc_log_rollsuffix_increment; + + obj = NULL; + CHECKM(named_config_get(maps, "dnstap-output", &obj), + "'dnstap-output' must be set if 'dnstap' is set"); + + obj2 = cfg_tuple_get(obj, "mode"); + if (obj2 == NULL) { + CHECKM(ISC_R_FAILURE, "dnstap-output mode not found"); + } + if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0) { + dmode = dns_dtmode_file; + } else { + dmode = dns_dtmode_unix; + } + + obj2 = cfg_tuple_get(obj, "path"); + if (obj2 == NULL) { + CHECKM(ISC_R_FAILURE, "dnstap-output path not found"); + } + + dpath = cfg_obj_asstring(obj2); + + obj2 = cfg_tuple_get(obj, "size"); + if (obj2 != NULL && cfg_obj_isuint64(obj2)) { + max_size = cfg_obj_asuint64(obj2); + if (max_size > SIZE_MAX) { + cfg_obj_log(obj2, named_g_lctx, ISC_LOG_WARNING, + "'dnstap-output size " + "%" PRIu64 "' " + "is too large for this " + "system; reducing to %lu", + max_size, (unsigned long)SIZE_MAX); + max_size = SIZE_MAX; + } + } + + obj2 = cfg_tuple_get(obj, "versions"); + if (obj2 != NULL && cfg_obj_isuint32(obj2)) { + rolls = cfg_obj_asuint32(obj2); + } else { + rolls = ISC_LOG_ROLLINFINITE; + } + + obj2 = cfg_tuple_get(obj, "suffix"); + if (obj2 != NULL && cfg_obj_isstring(obj2) && + strcasecmp(cfg_obj_asstring(obj2), "timestamp") == 0) + { + suffix = isc_log_rollsuffix_timestamp; + } + + fopt = fstrm_iothr_options_init(); + /* + * Both network threads and worker threads may log dnstap data. + */ + fstrm_iothr_options_set_num_input_queues(fopt, + 2 * named_g_cpus); + fstrm_iothr_options_set_queue_model( + fopt, FSTRM_IOTHR_QUEUE_MODEL_MPSC); + + obj = NULL; + result = named_config_get(maps, "fstrm-set-buffer-hint", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + fstrm_iothr_options_set_buffer_hint(fopt, i); + } + + obj = NULL; + result = named_config_get(maps, "fstrm-set-flush-timeout", + &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + fstrm_iothr_options_set_flush_timeout(fopt, i); + } + + obj = NULL; + result = named_config_get(maps, "fstrm-set-input-queue-size", + &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + fstrm_iothr_options_set_input_queue_size(fopt, i); + } + + obj = NULL; + result = named_config_get( + maps, "fstrm-set-output-notify-threshold", &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + fstrm_iothr_options_set_queue_notify_threshold(fopt, i); + } + + obj = NULL; + result = named_config_get(maps, "fstrm-set-output-queue-model", + &obj); + if (result == ISC_R_SUCCESS) { + if (strcasecmp(cfg_obj_asstring(obj), "spsc") == 0) { + i = FSTRM_IOTHR_QUEUE_MODEL_SPSC; + } else { + i = FSTRM_IOTHR_QUEUE_MODEL_MPSC; + } + fstrm_iothr_options_set_queue_model(fopt, i); + } + + obj = NULL; + result = named_config_get(maps, "fstrm-set-output-queue-size", + &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asuint32(obj); + fstrm_iothr_options_set_output_queue_size(fopt, i); + } + + obj = NULL; + result = named_config_get(maps, "fstrm-set-reopen-interval", + &obj); + if (result == ISC_R_SUCCESS) { + i = cfg_obj_asduration(obj); + fstrm_iothr_options_set_reopen_interval(fopt, i); + } + + CHECKM(dns_dt_create(named_g_mctx, dmode, dpath, &fopt, + named_g_server->task, + &named_g_server->dtenv), + "unable to create dnstap environment"); + + CHECKM(dns_dt_setupfile(named_g_server->dtenv, max_size, rolls, + suffix), + "unable to set up dnstap logfile"); + } + + if (named_g_server->dtenv == NULL) { + return (ISC_R_SUCCESS); + } + + obj = NULL; + result = named_config_get(maps, "dnstap-version", &obj); + if (result != ISC_R_SUCCESS) { + /* not specified; use the product and version */ + dns_dt_setversion(named_g_server->dtenv, PACKAGE_STRING); + } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + /* Quoted string */ + dns_dt_setversion(named_g_server->dtenv, cfg_obj_asstring(obj)); + } + + obj = NULL; + result = named_config_get(maps, "dnstap-identity", &obj); + if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { + /* "hostname" is interpreted as boolean true */ + char buf[256]; + if (gethostname(buf, sizeof(buf)) == 0) { + dns_dt_setidentity(named_g_server->dtenv, buf); + } + } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + /* Quoted string */ + dns_dt_setidentity(named_g_server->dtenv, + cfg_obj_asstring(obj)); + } + + dns_dt_attach(named_g_server->dtenv, &view->dtenv); + view->dttypes = dttypes; + + result = ISC_R_SUCCESS; + +cleanup: + if (fopt != NULL) { + fstrm_iothr_options_destroy(&fopt); + } + + return (result); +} +#endif /* HAVE_DNSTAP */ + +static isc_result_t +create_mapped_acl(void) { + isc_result_t result; + dns_acl_t *acl = NULL; + struct in6_addr in6 = IN6ADDR_V4MAPPED_INIT; + isc_netaddr_t addr; + + isc_netaddr_fromin6(&addr, &in6); + + result = dns_acl_create(named_g_mctx, 1, &acl); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = dns_iptable_addprefix(acl->iptable, &addr, 96, true); + if (result == ISC_R_SUCCESS) { + dns_acl_attach(acl, &named_g_mapped); + } + dns_acl_detach(&acl); + return (result); +} + +/*% + * A callback for the cfg_pluginlist_foreach() call in configure_view() below. + * If registering any plugin fails, registering subsequent ones is not + * attempted. + */ +static isc_result_t +register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj, + const char *plugin_path, const char *parameters, + void *callback_data) { + dns_view_t *view = callback_data; + char full_path[PATH_MAX]; + isc_result_t result; + + result = ns_plugin_expandpath(plugin_path, full_path, + sizeof(full_path)); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "%s: plugin configuration failed: " + "unable to get full plugin path: %s", + plugin_path, isc_result_totext(result)); + return (result); + } + + result = ns_plugin_register(full_path, parameters, config, + cfg_obj_file(obj), cfg_obj_line(obj), + named_g_mctx, named_g_lctx, + named_g_aclconfctx, view); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "%s: plugin configuration failed: %s", full_path, + isc_result_totext(result)); + } + + return (result); +} + +/* + * Determine if a minimal-sized cache can be used for a given view, according + * to 'maps' (implicit defaults, global options, view options) and 'optionmaps' + * (global options, view options). This is only allowed for views which have + * recursion disabled and do not have "max-cache-size" set explicitly. Using + * minimal-sized caches prevents a situation in which all explicitly configured + * and built-in views inherit the default "max-cache-size 90%;" setting, which + * could lead to memory exhaustion with multiple views configured. + */ +static bool +minimal_cache_allowed(const cfg_obj_t *maps[4], + const cfg_obj_t *optionmaps[3]) { + const cfg_obj_t *obj; + + /* + * Do not use a minimal-sized cache for a view with recursion enabled. + */ + obj = NULL; + (void)named_config_get(maps, "recursion", &obj); + INSIST(obj != NULL); + if (cfg_obj_asboolean(obj)) { + return (false); + } + + /* + * Do not use a minimal-sized cache if a specific size was requested. + */ + obj = NULL; + (void)named_config_get(optionmaps, "max-cache-size", &obj); + if (obj != NULL) { + return (false); + } + + return (true); +} + +static const char *const response_synonyms[] = { "response", NULL }; + +static const dns_name_t * +algorithm_name(unsigned int alg) { + switch (alg) { + case DST_ALG_HMACMD5: + return (dns_tsig_hmacmd5_name); + case DST_ALG_HMACSHA1: + return (dns_tsig_hmacsha1_name); + case DST_ALG_HMACSHA224: + return (dns_tsig_hmacsha224_name); + case DST_ALG_HMACSHA256: + return (dns_tsig_hmacsha256_name); + case DST_ALG_HMACSHA384: + return (dns_tsig_hmacsha384_name); + case DST_ALG_HMACSHA512: + return (dns_tsig_hmacsha512_name); + case DST_ALG_GSSAPI: + return (dns_tsig_gssapi_name); + default: + UNREACHABLE(); + } +} + +/* + * Configure 'view' according to 'vconfig', taking defaults from + * 'config' where values are missing in 'vconfig'. + * + * When configuring the default view, 'vconfig' will be NULL and the + * global defaults in 'config' used exclusively. + */ +static isc_result_t +configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, + cfg_obj_t *vconfig, named_cachelist_t *cachelist, + dns_kasplist_t *kasplist, const cfg_obj_t *bindkeys, + isc_mem_t *mctx, cfg_aclconfctx_t *actx, bool need_hints) { + const cfg_obj_t *maps[4]; + const cfg_obj_t *cfgmaps[3]; + const cfg_obj_t *optionmaps[3]; + const cfg_obj_t *options = NULL; + const cfg_obj_t *voptions = NULL; + const cfg_obj_t *forwardtype; + const cfg_obj_t *forwarders; + const cfg_obj_t *alternates; + const cfg_obj_t *zonelist; + const cfg_obj_t *dlzlist; + const cfg_obj_t *dlz; + const cfg_obj_t *prefetch_trigger; + const cfg_obj_t *prefetch_eligible; + unsigned int dlzargc; + char **dlzargv; + const cfg_obj_t *dyndb_list, *plugin_list; + const cfg_obj_t *disabled; + const cfg_obj_t *obj, *obj2; + const cfg_listelt_t *element = NULL; + const cfg_listelt_t *zone_element_latest = NULL; + in_port_t port; + dns_cache_t *cache = NULL; + isc_result_t result; + size_t max_cache_size; + uint32_t max_cache_size_percent = 0; + size_t max_adb_size; + uint32_t lame_ttl, fail_ttl; + uint32_t max_stale_ttl = 0; + uint32_t stale_refresh_time = 0; + dns_tsig_keyring_t *ring = NULL; + dns_transport_list_t *transports = NULL; + dns_view_t *pview = NULL; /* Production view */ + isc_mem_t *cmctx = NULL, *hmctx = NULL; + dns_dispatch_t *dispatch4 = NULL; + dns_dispatch_t *dispatch6 = NULL; + bool rpz_configured = false; + bool catz_configured = false; + bool shared_cache = false; + int i = 0, j = 0, k = 0; + const char *str; + const char *cachename = NULL; + dns_order_t *order = NULL; + uint32_t udpsize; + uint32_t maxbits; + unsigned int resopts = 0; + dns_zone_t *zone = NULL; + uint32_t max_clients_per_query; + bool empty_zones_enable; + const cfg_obj_t *disablelist = NULL; + isc_stats_t *resstats = NULL; + dns_stats_t *resquerystats = NULL; + bool auto_root = false; + named_cache_t *nsc; + bool zero_no_soattl; + dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL; + unsigned int query_timeout, ndisp; + bool old_rpz_ok = false; + dns_dyndbctx_t *dctx = NULL; + unsigned int resolver_param; + dns_ntatable_t *ntatable = NULL; + const char *qminmode = NULL; + + REQUIRE(DNS_VIEW_VALID(view)); + + if (config != NULL) { + (void)cfg_map_get(config, "options", &options); + } + + /* + * maps: view options, options, defaults + * cfgmaps: view options, config + * optionmaps: view options, options + */ + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + maps[i++] = voptions; + optionmaps[j++] = voptions; + cfgmaps[k++] = voptions; + } + if (options != NULL) { + maps[i++] = options; + optionmaps[j++] = options; + } + + maps[i++] = named_g_defaults; + maps[i] = NULL; + optionmaps[j] = NULL; + if (config != NULL) { + cfgmaps[k++] = config; + } + cfgmaps[k] = NULL; + + /* + * Set the view's port number for outgoing queries. + */ + CHECKM(named_config_getport(config, "port", &port), "port"); + dns_view_setdstport(view, port); + + /* + * Make the list of response policy zone names for a view that + * is used for real lookups and so cares about hints. + */ + obj = NULL; + if (view->rdclass == dns_rdataclass_in && need_hints && + named_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) + { + CHECK(configure_rpz(view, NULL, maps, obj, &old_rpz_ok)); + rpz_configured = true; + } + + obj = NULL; + if (view->rdclass != dns_rdataclass_in && need_hints && + named_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS) + { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "'catalog-zones' option is only supported " + "for views with class IN"); + } + + obj = NULL; + if (view->rdclass == dns_rdataclass_in && need_hints && + named_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS) + { + CHECK(configure_catz(view, NULL, config, obj)); + catz_configured = true; + } + + /* + * Configure the zones. + */ + zonelist = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "zone", &zonelist); + } else { + (void)cfg_map_get(config, "zone", &zonelist); + } + + /* + * Load zone configuration + */ + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig = cfg_listelt_value(element); + CHECK(configure_zone(config, zconfig, vconfig, mctx, view, + viewlist, kasplist, actx, false, + old_rpz_ok, false)); + zone_element_latest = element; + } + + /* + * Check that a primary or secondary zone was found for each + * zone named in the response policy statement, unless we are + * using RPZ service interface. + */ + if (view->rpzs != NULL && !view->rpzs->p.dnsrps_enabled) { + dns_rpz_num_t n; + + for (n = 0; n < view->rpzs->p.num_zones; ++n) { + if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(&view->rpzs->zones[n]->origin, + namebuf, sizeof(namebuf)); + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + DNS_RPZ_ERROR_LEVEL, + "rpz '%s' is not a primary or a " + "secondary zone", + namebuf); + result = ISC_R_NOTFOUND; + goto cleanup; + } + } + } + + /* + * If we're allowing added zones, then load zone configuration + * from the newzone file for zones that were added during previous + * runs. + */ + CHECK(configure_newzones(view, config, vconfig, mctx, actx)); + + /* + * Create Dynamically Loadable Zone driver. + */ + dlzlist = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "dlz", &dlzlist); + } else { + (void)cfg_map_get(config, "dlz", &dlzlist); + } + + for (element = cfg_list_first(dlzlist); element != NULL; + element = cfg_list_next(element)) + { + dlz = cfg_listelt_value(element); + + obj = NULL; + (void)cfg_map_get(dlz, "database", &obj); + if (obj != NULL) { + dns_dlzdb_t *dlzdb = NULL; + const cfg_obj_t *name, *search = NULL; + char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); + + if (s == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = isc_commandline_strtoargv(mctx, s, &dlzargc, + &dlzargv, 0); + if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, s); + goto cleanup; + } + + name = cfg_map_getname(dlz); + result = dns_dlzcreate(mctx, cfg_obj_asstring(name), + dlzargv[0], dlzargc, dlzargv, + &dlzdb); + isc_mem_free(mctx, s); + isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv)); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * If the DLZ backend supports configuration, + * and is searchable, then call its configure + * method now. If not searchable, we'll take + * care of it when we process the zone statement. + */ + (void)cfg_map_get(dlz, "search", &search); + if (search == NULL || cfg_obj_asboolean(search)) { + dlzdb->search = true; + result = dns_dlzconfigure( + view, dlzdb, dlzconfigure_callback); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + ISC_LIST_APPEND(view->dlz_searched, dlzdb, + link); + } else { + dlzdb->search = false; + ISC_LIST_APPEND(view->dlz_unsearched, dlzdb, + link); + } + } + } + + /* + * Obtain configuration parameters that affect the decision of whether + * we can reuse/share an existing cache. + */ + obj = NULL; + result = named_config_get(maps, "max-cache-size", &obj); + INSIST(result == ISC_R_SUCCESS); + /* + * If "-T maxcachesize=..." is in effect, it overrides any other + * "max-cache-size" setting found in configuration, either implicit or + * explicit. For simplicity, the value passed to that command line + * option is always treated as the number of bytes to set + * "max-cache-size" to. + */ + if (named_g_maxcachesize != 0) { + max_cache_size = named_g_maxcachesize; + } else if (minimal_cache_allowed(maps, optionmaps)) { + /* + * dns_cache_setcachesize() will adjust this to the smallest + * allowed value. + */ + max_cache_size = 1; + } else if (cfg_obj_isstring(obj)) { + str = cfg_obj_asstring(obj); + INSIST(strcasecmp(str, "unlimited") == 0); + max_cache_size = 0; + } else if (cfg_obj_ispercentage(obj)) { + max_cache_size = SIZE_AS_PERCENT; + max_cache_size_percent = cfg_obj_aspercentage(obj); + } else { + isc_resourcevalue_t value; + value = cfg_obj_asuint64(obj); + if (value > SIZE_MAX) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "'max-cache-size " + "%" PRIu64 "' " + "is too large for this " + "system; reducing to %lu", + value, (unsigned long)SIZE_MAX); + value = SIZE_MAX; + } + max_cache_size = (size_t)value; + } + + if (max_cache_size == SIZE_AS_PERCENT) { + uint64_t totalphys = isc_meminfo_totalphys(); + + max_cache_size = + (size_t)(totalphys * max_cache_size_percent / 100); + if (totalphys == 0) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "Unable to determine amount of physical " + "memory, setting 'max-cache-size' to " + "unlimited"); + } else { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO, + "'max-cache-size %d%%' " + "- setting to %" PRIu64 "MB " + "(out of %" PRIu64 "MB)", + max_cache_size_percent, + (uint64_t)(max_cache_size / (1024 * 1024)), + totalphys / (1024 * 1024)); + } + } + + /* Check-names. */ + obj = NULL; + result = named_checknames_get(maps, response_synonyms, &obj); + INSIST(result == ISC_R_SUCCESS); + + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "fail") == 0) { + resopts |= DNS_RESOLVER_CHECKNAMES | + DNS_RESOLVER_CHECKNAMESFAIL; + view->checknames = true; + } else if (strcasecmp(str, "warn") == 0) { + resopts |= DNS_RESOLVER_CHECKNAMES; + view->checknames = false; + } else if (strcasecmp(str, "ignore") == 0) { + view->checknames = false; + } else { + UNREACHABLE(); + } + + obj = NULL; + result = named_config_get(maps, "zero-no-soa-ttl-cache", &obj); + INSIST(result == ISC_R_SUCCESS); + zero_no_soattl = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "dns64", &obj); + if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") && + strcmp(view->name, "_meta")) + { + isc_netaddr_t na, suffix, *sp; + unsigned int prefixlen; + const char *server, *contact; + const cfg_obj_t *myobj; + + myobj = NULL; + result = named_config_get(maps, "dns64-server", &myobj); + if (result == ISC_R_SUCCESS) { + server = cfg_obj_asstring(myobj); + } else { + server = NULL; + } + + myobj = NULL; + result = named_config_get(maps, "dns64-contact", &myobj); + if (result == ISC_R_SUCCESS) { + contact = cfg_obj_asstring(myobj); + } else { + contact = NULL; + } + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *map = cfg_listelt_value(element); + dns_dns64_t *dns64 = NULL; + unsigned int dns64options = 0; + + cfg_obj_asnetprefix(cfg_map_getname(map), &na, + &prefixlen); + + obj = NULL; + (void)cfg_map_get(map, "suffix", &obj); + if (obj != NULL) { + sp = &suffix; + isc_netaddr_fromsockaddr( + sp, cfg_obj_assockaddr(obj)); + } else { + sp = NULL; + } + + clients = mapped = excluded = NULL; + obj = NULL; + (void)cfg_map_get(map, "clients", &obj); + if (obj != NULL) { + result = cfg_acl_fromconfig(obj, config, + named_g_lctx, actx, + mctx, 0, &clients); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + obj = NULL; + (void)cfg_map_get(map, "mapped", &obj); + if (obj != NULL) { + result = cfg_acl_fromconfig(obj, config, + named_g_lctx, actx, + mctx, 0, &mapped); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + obj = NULL; + (void)cfg_map_get(map, "exclude", &obj); + if (obj != NULL) { + result = cfg_acl_fromconfig(obj, config, + named_g_lctx, actx, + mctx, 0, &excluded); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } else { + if (named_g_mapped == NULL) { + result = create_mapped_acl(); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + dns_acl_attach(named_g_mapped, &excluded); + } + + obj = NULL; + (void)cfg_map_get(map, "recursive-only", &obj); + if (obj != NULL && cfg_obj_asboolean(obj)) { + dns64options |= DNS_DNS64_RECURSIVE_ONLY; + } + + obj = NULL; + (void)cfg_map_get(map, "break-dnssec", &obj); + if (obj != NULL && cfg_obj_asboolean(obj)) { + dns64options |= DNS_DNS64_BREAK_DNSSEC; + } + + result = dns_dns64_create(mctx, &na, prefixlen, sp, + clients, mapped, excluded, + dns64options, &dns64); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + dns_dns64_append(&view->dns64, dns64); + view->dns64cnt++; + result = dns64_reverse(view, mctx, &na, prefixlen, + server, contact); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + if (clients != NULL) { + dns_acl_detach(&clients); + } + if (mapped != NULL) { + dns_acl_detach(&mapped); + } + if (excluded != NULL) { + dns_acl_detach(&excluded); + } + } + } + + obj = NULL; + result = named_config_get(maps, "dnssec-accept-expired", &obj); + INSIST(result == ISC_R_SUCCESS); + view->acceptexpired = cfg_obj_asboolean(obj); + + obj = NULL; + /* 'optionmaps', not 'maps': don't check named_g_defaults yet */ + (void)named_config_get(optionmaps, "dnssec-validation", &obj); + if (obj == NULL) { + /* + * Default to VALIDATION_DEFAULT as set in config.c. + */ + (void)cfg_map_get(named_g_defaults, "dnssec-validation", &obj); + INSIST(obj != NULL); + } + if (obj != NULL) { + if (cfg_obj_isboolean(obj)) { + view->enablevalidation = cfg_obj_asboolean(obj); + } else { + /* + * If dnssec-validation is set but not boolean, + * then it must be "auto" + */ + view->enablevalidation = true; + auto_root = true; + } + } + + obj = NULL; + result = named_config_get(maps, "max-cache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->maxcachettl = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "max-ncache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->maxncachettl = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "min-cache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->mincachettl = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "min-ncache-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->minncachettl = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "synth-from-dnssec", &obj); + INSIST(result == ISC_R_SUCCESS); + view->synthfromdnssec = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "stale-cache-enable", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_asboolean(obj)) { + obj = NULL; + result = named_config_get(maps, "max-stale-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1); + } + /* + * If 'stale-cache-enable' is false, max_stale_ttl is set to 0, + * meaning keeping stale RRsets in cache is disabled. + */ + + obj = NULL; + result = named_config_get(maps, "stale-answer-enable", &obj); + INSIST(result == ISC_R_SUCCESS); + view->staleanswersenable = cfg_obj_asboolean(obj); + + result = dns_viewlist_find(&named_g_server->viewlist, view->name, + view->rdclass, &pview); + if (result == ISC_R_SUCCESS) { + view->staleanswersok = pview->staleanswersok; + dns_view_detach(&pview); + } else { + view->staleanswersok = dns_stale_answer_conf; + } + + obj = NULL; + result = named_config_get(maps, "stale-answer-client-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_isstring(obj)) { + /* + * The only string values available for this option + * are "disabled" and "off". + * We use (uint32_t) -1 to represent disabled since + * a value of zero means that stale data can be used + * to promptly answer the query, while an attempt to + * refresh the RRset will still be made in background. + */ + view->staleanswerclienttimeout = (uint32_t)-1; + } else { + view->staleanswerclienttimeout = cfg_obj_asuint32(obj); + } + + obj = NULL; + result = named_config_get(maps, "stale-refresh-time", &obj); + INSIST(result == ISC_R_SUCCESS); + stale_refresh_time = cfg_obj_asduration(obj); + + /* + * Configure the view's cache. + * + * First, check to see if there are any attach-cache options. If yes, + * attempt to lookup an existing cache at attach it to the view. If + * there is not one, then try to reuse an existing cache if possible; + * otherwise create a new cache. + * + * Note that the ADB is not preserved or shared in either case. + * + * When a matching view is found, the associated statistics are also + * retrieved and reused. + * + * XXX Determining when it is safe to reuse or share a cache is tricky. + * When the view's configuration changes, the cached data may become + * invalid because it reflects our old view of the world. We check + * some of the configuration parameters that could invalidate the cache + * or otherwise make it unshareable, but there are other configuration + * options that should be checked. For example, if a view uses a + * forwarder, changes in the forwarder configuration may invalidate + * the cache. At the moment, it's the administrator's responsibility to + * ensure these configuration options don't invalidate reusing/sharing. + */ + obj = NULL; + result = named_config_get(maps, "attach-cache", &obj); + if (result == ISC_R_SUCCESS) { + cachename = cfg_obj_asstring(obj); + } else { + cachename = view->name; + } + cache = NULL; + nsc = cachelist_find(cachelist, cachename, view->rdclass); + if (nsc != NULL) { + if (!cache_sharable(nsc->primaryview, view, zero_no_soattl, + max_cache_size, max_stale_ttl, + stale_refresh_time)) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "views %s and %s can't share the cache " + "due to configuration parameter mismatch", + nsc->primaryview->name, view->name); + result = ISC_R_FAILURE; + goto cleanup; + } + dns_cache_attach(nsc->cache, &cache); + shared_cache = true; + } else { + if (strcmp(cachename, view->name) == 0) { + result = dns_viewlist_find(&named_g_server->viewlist, + cachename, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + { + goto cleanup; + } + if (pview != NULL) { + if (!cache_reusable(pview, view, + zero_no_soattl)) + { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + ISC_LOG_DEBUG(1), + "cache cannot be reused " + "for view %s due to " + "configuration parameter " + "mismatch", + view->name); + } else { + INSIST(pview->cache != NULL); + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + ISC_LOG_DEBUG(3), + "reusing existing cache"); + dns_cache_attach(pview->cache, &cache); + } + dns_view_getresstats(pview, &resstats); + dns_view_getresquerystats(pview, + &resquerystats); + dns_view_detach(&pview); + } + } + if (cache == NULL) { + /* + * Create a cache with the desired name. This normally + * equals the view name, but may also be a forward + * reference to a view that share the cache with this + * view but is not yet configured. If it is not the + * view name but not a forward reference either, then it + * is simply a named cache that is not shared. + * + * We use two separate memory contexts for the + * cache, for the main cache memory and the heap + * memory. + */ + isc_mem_create(&cmctx); + isc_mem_setname(cmctx, "cache"); + isc_mem_create(&hmctx); + isc_mem_setname(hmctx, "cache_heap"); + CHECK(dns_cache_create(cmctx, hmctx, named_g_taskmgr, + named_g_timermgr, view->rdclass, + cachename, "rbt", 0, NULL, + &cache)); + isc_mem_detach(&cmctx); + isc_mem_detach(&hmctx); + } + nsc = isc_mem_get(mctx, sizeof(*nsc)); + nsc->cache = NULL; + dns_cache_attach(cache, &nsc->cache); + nsc->primaryview = view; + nsc->needflush = false; + nsc->adbsizeadjusted = false; + nsc->rdclass = view->rdclass; + ISC_LINK_INIT(nsc, link); + ISC_LIST_APPEND(*cachelist, nsc, link); + } + dns_view_setcache(view, cache, shared_cache); + + dns_cache_setcachesize(cache, max_cache_size); + dns_cache_setservestalettl(cache, max_stale_ttl); + dns_cache_setservestalerefresh(cache, stale_refresh_time); + + dns_cache_detach(&cache); + + obj = NULL; + result = named_config_get(maps, "stale-answer-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + view->staleanswerttl = ISC_MAX(cfg_obj_asduration(obj), 1); + + /* + * Resolver. + */ + CHECK(get_view_querysource_dispatch( + maps, AF_INET, &dispatch4, + (ISC_LIST_PREV(view, link) == NULL))); + CHECK(get_view_querysource_dispatch( + maps, AF_INET6, &dispatch6, + (ISC_LIST_PREV(view, link) == NULL))); + if (dispatch4 == NULL && dispatch6 == NULL) { + UNEXPECTED_ERROR("unable to obtain either an IPv4 or" + " an IPv6 dispatch"); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + if (resstats == NULL) { + CHECK(isc_stats_create(mctx, &resstats, + dns_resstatscounter_max)); + } + dns_view_setresstats(view, resstats); + if (resquerystats == NULL) { + CHECK(dns_rdatatypestats_create(mctx, &resquerystats)); + } + dns_view_setresquerystats(view, resquerystats); + + ndisp = 4 * ISC_MIN(named_g_udpdisp, MAX_UDP_DISPATCH); + CHECK(dns_view_createresolver( + view, named_g_taskmgr, RESOLVER_NTASKS_PERCPU * named_g_cpus, + ndisp, named_g_netmgr, named_g_timermgr, resopts, + named_g_dispatchmgr, dispatch4, dispatch6)); + + /* + * Set the ADB cache size to 1/8th of the max-cache-size or + * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared. + */ + max_adb_size = 0; + if (max_cache_size != 0U) { + max_adb_size = max_cache_size / 8; + if (max_adb_size == 0U) { + max_adb_size = 1; /* Force minimum. */ + } + if (view != nsc->primaryview && + max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) + { + max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE; + if (!nsc->adbsizeadjusted) { + dns_adb_setadbsize(nsc->primaryview->adb, + MAX_ADB_SIZE_FOR_CACHESHARE); + nsc->adbsizeadjusted = true; + } + } + } + dns_adb_setadbsize(view->adb, max_adb_size); + + /* + * Set up ADB quotas + */ + { + uint32_t fps, freq; + double low, high, discount; + + obj = NULL; + result = named_config_get(maps, "fetches-per-server", &obj); + INSIST(result == ISC_R_SUCCESS); + obj2 = cfg_tuple_get(obj, "fetches"); + fps = cfg_obj_asuint32(obj2); + obj2 = cfg_tuple_get(obj, "response"); + if (!cfg_obj_isvoid(obj2)) { + const char *resp = cfg_obj_asstring(obj2); + isc_result_t r = DNS_R_SERVFAIL; + + if (strcasecmp(resp, "drop") == 0) { + r = DNS_R_DROP; + } else if (strcasecmp(resp, "fail") == 0) { + r = DNS_R_SERVFAIL; + } else { + UNREACHABLE(); + } + + dns_resolver_setquotaresponse(view->resolver, + dns_quotatype_server, r); + } + + obj = NULL; + result = named_config_get(maps, "fetch-quota-params", &obj); + INSIST(result == ISC_R_SUCCESS); + + obj2 = cfg_tuple_get(obj, "frequency"); + freq = cfg_obj_asuint32(obj2); + + obj2 = cfg_tuple_get(obj, "low"); + low = (double)cfg_obj_asfixedpoint(obj2) / 100.0; + + obj2 = cfg_tuple_get(obj, "high"); + high = (double)cfg_obj_asfixedpoint(obj2) / 100.0; + + obj2 = cfg_tuple_get(obj, "discount"); + discount = (double)cfg_obj_asfixedpoint(obj2) / 100.0; + + dns_adb_setquota(view->adb, fps, freq, low, high, discount); + } + + /* + * Set resolver's lame-ttl. + */ + obj = NULL; + result = named_config_get(maps, "lame-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + lame_ttl = cfg_obj_asduration(obj); + if (lame_ttl > 0) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "disabling lame cache despite lame-ttl > 0 as it " + "may cause performance issues"); + lame_ttl = 0; + } + dns_resolver_setlamettl(view->resolver, lame_ttl); + + /* + * Set the resolver's query timeout. + */ + obj = NULL; + result = named_config_get(maps, "resolver-query-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + query_timeout = cfg_obj_asuint32(obj); + dns_resolver_settimeout(view->resolver, query_timeout); + + /* + * Adjust stale-answer-client-timeout upper bound + * to be resolver-query-timeout - 1s. + * This assignment is safe as dns_resolver_settimeout() + * ensures that resolver->querytimeout value will be in the + * [MINIMUM_QUERY_TIMEOUT, MAXIMUM_QUERY_TIMEOUT] range and + * MINIMUM_QUERY_TIMEOUT is > 1000 (in ms). + */ + if (view->staleanswerclienttimeout != (uint32_t)-1 && + view->staleanswerclienttimeout > + (dns_resolver_gettimeout(view->resolver) - 1000)) + { + view->staleanswerclienttimeout = + dns_resolver_gettimeout(view->resolver) - 1000; + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "stale-answer-client-timeout adjusted to %" PRIu32, + view->staleanswerclienttimeout); + } + + /* Specify whether to use 0-TTL for negative response for SOA query */ + dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl); + + /* + * Set the resolver's EDNS UDP size. + */ + obj = NULL; + result = named_config_get(maps, "edns-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512) { + udpsize = 512; + } + if (udpsize > 4096) { + udpsize = 4096; + } + dns_resolver_setudpsize(view->resolver, (uint16_t)udpsize); + + /* + * Set the maximum UDP response size. + */ + obj = NULL; + result = named_config_get(maps, "max-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512) { + udpsize = 512; + } + if (udpsize > 4096) { + udpsize = 4096; + } + view->maxudp = udpsize; + + /* + * Set the maximum UDP when a COOKIE is not provided. + */ + obj = NULL; + result = named_config_get(maps, "nocookie-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 128) { + udpsize = 128; + } + if (udpsize > view->maxudp) { + udpsize = view->maxudp; + } + view->nocookieudp = udpsize; + + /* + * Set the maximum rsa exponent bits. + */ + obj = NULL; + result = named_config_get(maps, "max-rsa-exponent-size", &obj); + INSIST(result == ISC_R_SUCCESS); + maxbits = cfg_obj_asuint32(obj); + if (maxbits != 0 && maxbits < 35) { + maxbits = 35; + } + if (maxbits > 4096) { + maxbits = 4096; + } + view->maxbits = maxbits; + + /* + * Set resolver retry parameters. + */ + obj = NULL; + CHECK(named_config_get(maps, "resolver-retry-interval", &obj)); + resolver_param = cfg_obj_asuint32(obj); + if (resolver_param > 0) { + dns_resolver_setretryinterval(view->resolver, resolver_param); + } + + obj = NULL; + CHECK(named_config_get(maps, "resolver-nonbackoff-tries", &obj)); + resolver_param = cfg_obj_asuint32(obj); + if (resolver_param > 0) { + dns_resolver_setnonbackofftries(view->resolver, resolver_param); + } + + /* + * Set supported DNSSEC algorithms. + */ + dns_resolver_reset_algorithms(view->resolver); + disabled = NULL; + (void)named_config_get(maps, "disable-algorithms", &disabled); + if (disabled != NULL) { + for (element = cfg_list_first(disabled); element != NULL; + element = cfg_list_next(element)) + { + CHECK(disable_algorithms(cfg_listelt_value(element), + view->resolver)); + } + } + + /* + * Set supported DS digest types. + */ + dns_resolver_reset_ds_digests(view->resolver); + disabled = NULL; + (void)named_config_get(maps, "disable-ds-digests", &disabled); + if (disabled != NULL) { + for (element = cfg_list_first(disabled); element != NULL; + element = cfg_list_next(element)) + { + CHECK(disable_ds_digests(cfg_listelt_value(element), + view->resolver)); + } + } + + /* + * A global or view "forwarders" option, if present, + * creates an entry for "." in the forwarding table. + */ + forwardtype = NULL; + forwarders = NULL; + (void)named_config_get(maps, "forward", &forwardtype); + (void)named_config_get(maps, "forwarders", &forwarders); + if (forwarders != NULL) { + CHECK(configure_forward(config, view, dns_rootname, forwarders, + forwardtype)); + } + + /* + * Dual Stack Servers. + */ + alternates = NULL; + (void)named_config_get(maps, "dual-stack-servers", &alternates); + if (alternates != NULL) { + CHECK(configure_alternates(config, view, alternates)); + } + + /* + * We have default hints for class IN if we need them. + */ + if (view->rdclass == dns_rdataclass_in && view->hints == NULL) { + dns_view_sethints(view, named_g_server->in_roothints); + } + + /* + * If we still have no hints, this is a non-IN view with no + * "hints zone" configured. Issue a warning, except if this + * is a root server. Root servers never need to consult + * their hints, so it's no point requiring users to configure + * them. + */ + if (view->hints == NULL) { + dns_zone_t *rootzone = NULL; + (void)dns_view_findzone(view, dns_rootname, &rootzone); + if (rootzone != NULL) { + dns_zone_detach(&rootzone); + need_hints = false; + } + if (need_hints) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "no root hints for view '%s'", + view->name); + } + } + + /* + * Configure the view's transports (DoT/DoH) + */ + CHECK(named_transports_fromconfig(config, vconfig, view->mctx, + &transports)); + dns_view_settransports(view, transports); + dns_transport_list_detach(&transports); + + /* + * Configure the view's TSIG keys. + */ + CHECK(named_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring)); + if (named_g_server->sessionkey != NULL) { + dns_tsigkey_t *tsigkey = NULL; + result = dns_tsigkey_createfromkey( + named_g_server->session_keyname, + algorithm_name(named_g_server->session_keyalg), + named_g_server->sessionkey, false, NULL, 0, 0, mctx, + NULL, &tsigkey); + if (result == ISC_R_SUCCESS) { + result = dns_tsigkeyring_add( + ring, named_g_server->session_keyname, tsigkey); + dns_tsigkey_detach(&tsigkey); + } + CHECK(result); + } + dns_view_setkeyring(view, ring); + dns_tsigkeyring_detach(&ring); + + /* + * See if we can re-use a dynamic key ring. + */ + result = dns_viewlist_find(&named_g_server->viewlist, view->name, + view->rdclass, &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { + goto cleanup; + } + if (pview != NULL) { + dns_view_getdynamickeyring(pview, &ring); + if (ring != NULL) { + dns_view_setdynamickeyring(view, ring); + } + dns_tsigkeyring_detach(&ring); + dns_view_detach(&pview); + } else { + dns_view_restorekeyring(view); + } + + /* + * Configure the view's peer list. + */ + { + const cfg_obj_t *peers = NULL; + dns_peerlist_t *newpeers = NULL; + + (void)named_config_get(cfgmaps, "server", &peers); + CHECK(dns_peerlist_new(mctx, &newpeers)); + for (element = cfg_list_first(peers); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *cpeer = cfg_listelt_value(element); + dns_peer_t *peer; + + CHECK(configure_peer(cpeer, mctx, &peer)); + dns_peerlist_addpeer(newpeers, peer); + dns_peer_detach(&peer); + } + dns_peerlist_detach(&view->peers); + view->peers = newpeers; /* Transfer ownership. */ + } + + /* + * Configure the views rrset-order. + */ + { + const cfg_obj_t *rrsetorder = NULL; + + (void)named_config_get(maps, "rrset-order", &rrsetorder); + CHECK(dns_order_create(mctx, &order)); + for (element = cfg_list_first(rrsetorder); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *ent = cfg_listelt_value(element); + + CHECK(configure_order(order, ent)); + } + if (view->order != NULL) { + dns_order_detach(&view->order); + } + dns_order_attach(order, &view->order); + dns_order_detach(&order); + } + /* + * Copy the aclenv object. + */ + dns_aclenv_copy(view->aclenv, ns_interfacemgr_getaclenv( + named_g_server->interfacemgr)); + + /* + * Configure the "match-clients" and "match-destinations" ACL. + * (These are only meaningful at the view level, but 'config' + * must be passed so that named ACLs defined at the global level + * can be retrieved.) + */ + CHECK(configure_view_acl(vconfig, config, NULL, "match-clients", NULL, + actx, named_g_mctx, &view->matchclients)); + CHECK(configure_view_acl(vconfig, config, NULL, "match-destinations", + NULL, actx, named_g_mctx, + &view->matchdestinations)); + + /* + * Configure the "match-recursive-only" option. + */ + obj = NULL; + (void)named_config_get(maps, "match-recursive-only", &obj); + if (obj != NULL && cfg_obj_asboolean(obj)) { + view->matchrecursiveonly = true; + } else { + view->matchrecursiveonly = false; + } + + /* + * Configure other configurable data. + */ + obj = NULL; + result = named_config_get(maps, "recursion", &obj); + INSIST(result == ISC_R_SUCCESS); + view->recursion = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "qname-minimization", &obj); + INSIST(result == ISC_R_SUCCESS); + qminmode = cfg_obj_asstring(obj); + INSIST(qminmode != NULL); + if (!strcmp(qminmode, "strict")) { + view->qminimization = true; + view->qmin_strict = true; + } else if (!strcmp(qminmode, "relaxed")) { + view->qminimization = true; + view->qmin_strict = false; + } else { /* "disabled" or "off" */ + view->qminimization = false; + view->qmin_strict = false; + } + + obj = NULL; + result = named_config_get(maps, "auth-nxdomain", &obj); + INSIST(result == ISC_R_SUCCESS); + view->auth_nxdomain = cfg_obj_asboolean(obj); + + /* deprecated */ + obj = NULL; + result = named_config_get(maps, "glue-cache", &obj); + INSIST(result == ISC_R_SUCCESS); + view->use_glue_cache = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "minimal-any", &obj); + INSIST(result == ISC_R_SUCCESS); + view->minimal_any = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "minimal-responses", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + view->minimalresponses = dns_minimal_yes; + } else { + view->minimalresponses = dns_minimal_no; + } + } else { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "no-auth") == 0) { + view->minimalresponses = dns_minimal_noauth; + } else if (strcasecmp(str, "no-auth-recursive") == 0) { + view->minimalresponses = dns_minimal_noauthrec; + } else { + UNREACHABLE(); + } + } + + obj = NULL; + result = named_config_get(maps, "transfer-format", &obj); + INSIST(result == ISC_R_SUCCESS); + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "many-answers") == 0) { + view->transfer_format = dns_many_answers; + } else if (strcasecmp(str, "one-answer") == 0) { + view->transfer_format = dns_one_answer; + } else { + UNREACHABLE(); + } + + obj = NULL; + result = named_config_get(maps, "trust-anchor-telemetry", &obj); + INSIST(result == ISC_R_SUCCESS); + view->trust_anchor_telemetry = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "root-key-sentinel", &obj); + INSIST(result == ISC_R_SUCCESS); + view->root_key_sentinel = cfg_obj_asboolean(obj); + + /* + * Set the "allow-query", "allow-query-cache", "allow-recursion", + * "allow-recursion-on" and "allow-query-cache-on" ACLs if + * configured in named.conf, but NOT from the global defaults. + * This is done by leaving the third argument to configure_view_acl() + * NULL. + * + * We ignore the global defaults here because these ACLs + * can inherit from each other. If any are still unset after + * applying the inheritance rules, we'll look up the defaults at + * that time. + */ + + /* named.conf only */ + CHECK(configure_view_acl(vconfig, config, NULL, "allow-query", NULL, + actx, named_g_mctx, &view->queryacl)); + + /* named.conf only */ + CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache", + NULL, actx, named_g_mctx, &view->cacheacl)); + /* named.conf only */ + CHECK(configure_view_acl(vconfig, config, NULL, "allow-query-cache-on", + NULL, actx, named_g_mctx, &view->cacheonacl)); + + if (strcmp(view->name, "_bind") != 0 && + view->rdclass != dns_rdataclass_chaos) + { + /* named.conf only */ + CHECK(configure_view_acl(vconfig, config, NULL, + "allow-recursion", NULL, actx, + named_g_mctx, &view->recursionacl)); + /* named.conf only */ + CHECK(configure_view_acl(vconfig, config, NULL, + "allow-recursion-on", NULL, actx, + named_g_mctx, &view->recursiononacl)); + } + + if (view->recursion) { + /* + * "allow-query-cache" inherits from "allow-recursion" if set, + * otherwise from "allow-query" if set. + */ + if (view->cacheacl == NULL) { + if (view->recursionacl != NULL) { + dns_acl_attach(view->recursionacl, + &view->cacheacl); + } else if (view->queryacl != NULL) { + dns_acl_attach(view->queryacl, &view->cacheacl); + } + } + + /* + * "allow-recursion" inherits from "allow-query-cache" if set, + * otherwise from "allow-query" if set. + */ + if (view->recursionacl == NULL) { + if (view->cacheacl != NULL) { + dns_acl_attach(view->cacheacl, + &view->recursionacl); + } else if (view->queryacl != NULL) { + dns_acl_attach(view->queryacl, + &view->recursionacl); + } + } + + /* + * "allow-query-cache-on" inherits from "allow-recursion-on" + * if set. + */ + if (view->cacheonacl == NULL) { + if (view->recursiononacl != NULL) { + dns_acl_attach(view->recursiononacl, + &view->cacheonacl); + } + } + + /* + * "allow-recursion-on" inherits from "allow-query-cache-on" + * if set. + */ + if (view->recursiononacl == NULL) { + if (view->cacheonacl != NULL) { + dns_acl_attach(view->cacheonacl, + &view->recursiononacl); + } + } + + /* + * If any are still unset at this point, we now get default + * values for from the global config. + */ + + if (view->recursionacl == NULL) { + /* global default only */ + CHECK(configure_view_acl( + NULL, NULL, named_g_config, "allow-recursion", + NULL, actx, named_g_mctx, &view->recursionacl)); + } + if (view->recursiononacl == NULL) { + /* global default only */ + CHECK(configure_view_acl(NULL, NULL, named_g_config, + "allow-recursion-on", NULL, + actx, named_g_mctx, + &view->recursiononacl)); + } + if (view->cacheacl == NULL) { + /* global default only */ + CHECK(configure_view_acl( + NULL, NULL, named_g_config, "allow-query-cache", + NULL, actx, named_g_mctx, &view->cacheacl)); + } + if (view->cacheonacl == NULL) { + /* global default only */ + CHECK(configure_view_acl(NULL, NULL, named_g_config, + "allow-query-cache-on", NULL, + actx, named_g_mctx, + &view->cacheonacl)); + } + } else { + /* + * We're not recursive; if the query-cache ACLs haven't + * been set at the options/view level, set them to none. + */ + if (view->cacheacl == NULL) { + CHECK(dns_acl_none(mctx, &view->cacheacl)); + } + if (view->cacheonacl == NULL) { + CHECK(dns_acl_none(mctx, &view->cacheonacl)); + } + } + + /* + * Finished setting recursion and query-cache ACLs, so now we + * can get the allow-query default if it wasn't set in named.conf + */ + if (view->queryacl == NULL) { + /* global default only */ + CHECK(configure_view_acl(NULL, NULL, named_g_config, + "allow-query", NULL, actx, + named_g_mctx, &view->queryacl)); + } + + /* + * Ignore case when compressing responses to the specified + * clients. This causes case not always to be preserved, + * and is needed by some broken clients. + */ + CHECK(configure_view_acl(vconfig, config, named_g_config, + "no-case-compress", NULL, actx, named_g_mctx, + &view->nocasecompress)); + + /* + * Disable name compression completely, this is a tradeoff + * between CPU and network usage. + */ + obj = NULL; + result = named_config_get(maps, "message-compression", &obj); + INSIST(result == ISC_R_SUCCESS); + view->msgcompression = cfg_obj_asboolean(obj); + + /* + * Filter setting on addresses in the answer section. + */ + CHECK(configure_view_acl(vconfig, config, named_g_config, + "deny-answer-addresses", "acl", actx, + named_g_mctx, &view->denyansweracl)); + CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses", + "except-from", named_g_mctx, + &view->answeracl_exclude)); + + /* + * Filter setting on names (CNAME/DNAME targets) in the answer section. + */ + CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", + "name", named_g_mctx, + &view->denyanswernames)); + CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases", + "except-from", named_g_mctx, + &view->answernames_exclude)); + + /* + * Configure sortlist, if set + */ + CHECK(configure_view_sortlist(vconfig, config, actx, named_g_mctx, + &view->sortlist)); + + /* + * Configure default allow-update and allow-update-forwarding ACLs, + * so they can be inherited by zones. (XXX: These are not + * read from the options/view level here. However, they may be + * read from there in zoneconf.c:configure_zone_acl() later.) + */ + if (view->updateacl == NULL) { + CHECK(configure_view_acl(NULL, NULL, named_g_config, + "allow-update", NULL, actx, + named_g_mctx, &view->updateacl)); + } + if (view->upfwdacl == NULL) { + CHECK(configure_view_acl(NULL, NULL, named_g_config, + "allow-update-forwarding", NULL, actx, + named_g_mctx, &view->upfwdacl)); + } + + /* + * Configure default allow-transfer and allow-notify ACLs so they + * can be inherited by zones. + */ + if (view->transferacl == NULL) { + CHECK(configure_view_acl(vconfig, config, named_g_config, + "allow-transfer", NULL, actx, + named_g_mctx, &view->transferacl)); + } + if (view->notifyacl == NULL) { + CHECK(configure_view_acl(vconfig, config, named_g_config, + "allow-notify", NULL, actx, + named_g_mctx, &view->notifyacl)); + } + + obj = NULL; + result = named_config_get(maps, "provide-ixfr", &obj); + INSIST(result == ISC_R_SUCCESS); + view->provideixfr = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "request-nsid", &obj); + INSIST(result == ISC_R_SUCCESS); + view->requestnsid = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "send-cookie", &obj); + INSIST(result == ISC_R_SUCCESS); + view->sendcookie = cfg_obj_asboolean(obj); + + obj = NULL; + if (view->pad_acl != NULL) { + dns_acl_detach(&view->pad_acl); + } + result = named_config_get(optionmaps, "response-padding", &obj); + if (result == ISC_R_SUCCESS) { + const cfg_obj_t *padobj = cfg_tuple_get(obj, "block-size"); + const cfg_obj_t *aclobj = cfg_tuple_get(obj, "acl"); + uint32_t padding = cfg_obj_asuint32(padobj); + + if (padding > 512U) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "response-padding block-size cannot " + "exceed 512: lowering"); + padding = 512U; + } + view->padding = (uint16_t)padding; + CHECK(cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, + named_g_mctx, 0, &view->pad_acl)); + } + + obj = NULL; + result = named_config_get(maps, "require-server-cookie", &obj); + INSIST(result == ISC_R_SUCCESS); + view->requireservercookie = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "v6-bias", &obj); + INSIST(result == ISC_R_SUCCESS); + view->v6bias = cfg_obj_asuint32(obj) * 1000; + + obj = NULL; + result = named_config_get(maps, "max-clients-per-query", &obj); + INSIST(result == ISC_R_SUCCESS); + max_clients_per_query = cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "clients-per-query", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj), + max_clients_per_query); + + obj = NULL; + result = named_config_get(maps, "max-recursion-depth", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "max-recursion-queries", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "fetches-per-zone", &obj); + INSIST(result == ISC_R_SUCCESS); + obj2 = cfg_tuple_get(obj, "fetches"); + dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj2)); + obj2 = cfg_tuple_get(obj, "response"); + if (!cfg_obj_isvoid(obj2)) { + const char *resp = cfg_obj_asstring(obj2); + isc_result_t r = DNS_R_SERVFAIL; + + if (strcasecmp(resp, "drop") == 0) { + r = DNS_R_DROP; + } else if (strcasecmp(resp, "fail") == 0) { + r = DNS_R_SERVFAIL; + } else { + UNREACHABLE(); + } + + dns_resolver_setquotaresponse(view->resolver, + dns_quotatype_zone, r); + } + + obj = NULL; + result = named_config_get(maps, "prefetch", &obj); + INSIST(result == ISC_R_SUCCESS); + prefetch_trigger = cfg_tuple_get(obj, "trigger"); + view->prefetch_trigger = cfg_obj_asuint32(prefetch_trigger); + if (view->prefetch_trigger > 10) { + view->prefetch_trigger = 10; + } + prefetch_eligible = cfg_tuple_get(obj, "eligible"); + if (cfg_obj_isvoid(prefetch_eligible)) { + int m; + for (m = 1; maps[m] != NULL; m++) { + obj = NULL; + result = named_config_get(&maps[m], "prefetch", &obj); + INSIST(result == ISC_R_SUCCESS); + prefetch_eligible = cfg_tuple_get(obj, "eligible"); + if (cfg_obj_isuint32(prefetch_eligible)) { + break; + } + } + INSIST(cfg_obj_isuint32(prefetch_eligible)); + } + view->prefetch_eligible = cfg_obj_asuint32(prefetch_eligible); + if (view->prefetch_eligible < view->prefetch_trigger + 6) { + view->prefetch_eligible = view->prefetch_trigger + 6; + } + + /* + * For now, there is only one kind of trusted keys, the + * "security roots". + */ + CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys, + auto_root, mctx)); + dns_resolver_resetmustbesecure(view->resolver); + obj = NULL; + result = named_config_get(maps, "dnssec-must-be-secure", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(mustbesecure(obj, view->resolver)); + } + + obj = NULL; + result = named_config_get(maps, "nta-recheck", &obj); + INSIST(result == ISC_R_SUCCESS); + view->nta_recheck = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "nta-lifetime", &obj); + INSIST(result == ISC_R_SUCCESS); + view->nta_lifetime = cfg_obj_asduration(obj); + + obj = NULL; + result = named_config_get(maps, "preferred-glue", &obj); + if (result == ISC_R_SUCCESS) { + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "a") == 0) { + view->preferred_glue = dns_rdatatype_a; + } else if (strcasecmp(str, "aaaa") == 0) { + view->preferred_glue = dns_rdatatype_aaaa; + } else { + view->preferred_glue = 0; + } + } else { + view->preferred_glue = 0; + } + + obj = NULL; + result = named_config_get(maps, "root-delegation-only", &obj); + if (result == ISC_R_SUCCESS) { + dns_view_setrootdelonly(view, true); + } + if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + const cfg_obj_t *exclude; + dns_fixedname_t fixed; + dns_name_t *name; + + name = dns_fixedname_initname(&fixed); + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + exclude = cfg_listelt_value(element); + CHECK(dns_name_fromstring( + name, cfg_obj_asstring(exclude), 0, NULL)); + dns_view_excludedelegationonly(view, name); + } + } else { + dns_view_setrootdelonly(view, false); + } + + /* + * Load DynDB modules. + */ + dyndb_list = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "dyndb", &dyndb_list); + } else { + (void)cfg_map_get(config, "dyndb", &dyndb_list); + } + + for (element = cfg_list_first(dyndb_list); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *dyndb = cfg_listelt_value(element); + + if (dctx == NULL) { + const void *hashinit = isc_hash_get_initializer(); + CHECK(dns_dyndb_createctx(mctx, hashinit, named_g_lctx, + view, named_g_server->zonemgr, + named_g_server->task, + named_g_timermgr, &dctx)); + } + + CHECK(configure_dyndb(dyndb, mctx, dctx)); + } + + /* + * Load plugins. + */ + plugin_list = NULL; + if (voptions != NULL) { + (void)cfg_map_get(voptions, "plugin", &plugin_list); + } else { + (void)cfg_map_get(config, "plugin", &plugin_list); + } + + if (plugin_list != NULL) { + INSIST(view->hooktable == NULL); + CHECK(ns_hooktable_create(view->mctx, + (ns_hooktable_t **)&view->hooktable)); + view->hooktable_free = ns_hooktable_free; + + ns_plugins_create(view->mctx, (ns_plugins_t **)&view->plugins); + view->plugins_free = ns_plugins_free; + + CHECK(cfg_pluginlist_foreach(config, plugin_list, named_g_lctx, + register_one_plugin, view)); + } + + /* + * Setup automatic empty zones. If recursion is off then + * they are disabled by default. + */ + obj = NULL; + (void)named_config_get(maps, "empty-zones-enable", &obj); + (void)named_config_get(maps, "disable-empty-zone", &disablelist); + if (obj == NULL && disablelist == NULL && + view->rdclass == dns_rdataclass_in) + { + empty_zones_enable = view->recursion; + } else if (view->rdclass == dns_rdataclass_in) { + if (obj != NULL) { + empty_zones_enable = cfg_obj_asboolean(obj); + } else { + empty_zones_enable = view->recursion; + } + } else { + empty_zones_enable = false; + } + + if (empty_zones_enable) { + const char *empty; + int empty_zone = 0; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t buffer; + char server[DNS_NAME_FORMATSIZE + 1]; + char contact[DNS_NAME_FORMATSIZE + 1]; + const char *empty_dbtype[4] = { "_builtin", "empty", NULL, + NULL }; + int empty_dbtypec = 4; + dns_zonestat_level_t statlevel = dns_zonestat_none; + + name = dns_fixedname_initname(&fixed); + + obj = NULL; + result = named_config_get(maps, "empty-server", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj), + 0, NULL)); + isc_buffer_init(&buffer, server, sizeof(server) - 1); + CHECK(dns_name_totext(name, false, &buffer)); + server[isc_buffer_usedlength(&buffer)] = 0; + empty_dbtype[2] = server; + } else { + empty_dbtype[2] = "@"; + } + + obj = NULL; + result = named_config_get(maps, "empty-contact", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj), + 0, NULL)); + isc_buffer_init(&buffer, contact, sizeof(contact) - 1); + CHECK(dns_name_totext(name, false, &buffer)); + contact[isc_buffer_usedlength(&buffer)] = 0; + empty_dbtype[3] = contact; + } else { + empty_dbtype[3] = "."; + } + + obj = NULL; + result = named_config_get(maps, "zone-statistics", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + statlevel = dns_zonestat_full; + } else { + statlevel = dns_zonestat_none; + } + } else { + const char *levelstr = cfg_obj_asstring(obj); + if (strcasecmp(levelstr, "full") == 0) { + statlevel = dns_zonestat_full; + } else if (strcasecmp(levelstr, "terse") == 0) { + statlevel = dns_zonestat_terse; + } else if (strcasecmp(levelstr, "none") == 0) { + statlevel = dns_zonestat_none; + } else { + UNREACHABLE(); + } + } + + for (empty = empty_zones[empty_zone]; empty != NULL; + empty = empty_zones[++empty_zone]) + { + dns_forwarders_t *dnsforwarders = NULL; + + /* + * Look for zone on drop list. + */ + CHECK(dns_name_fromstring(name, empty, 0, NULL)); + if (disablelist != NULL && + on_disable_list(disablelist, name)) + { + continue; + } + + /* + * This zone already exists. + */ + (void)dns_view_findzone(view, name, &zone); + if (zone != NULL) { + dns_zone_detach(&zone); + continue; + } + + /* + * If we would forward this name don't add a + * empty zone for it. + */ + result = dns_fwdtable_find(view->fwdtable, name, NULL, + &dnsforwarders); + if ((result == ISC_R_SUCCESS || + result == DNS_R_PARTIALMATCH) && + dnsforwarders->fwdpolicy == dns_fwdpolicy_only) + { + continue; + } + + /* + * See if we can re-use a existing zone. + */ + result = dns_viewlist_find(&named_g_server->viewlist, + view->name, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + { + goto cleanup; + } + + if (pview != NULL) { + (void)dns_view_findzone(pview, name, &zone); + dns_view_detach(&pview); + } + + CHECK(create_empty_zone(zone, name, view, zonelist, + empty_dbtype, empty_dbtypec, + statlevel)); + if (zone != NULL) { + dns_zone_detach(&zone); + } + } + } + + obj = NULL; + if (view->rdclass == dns_rdataclass_in) { + (void)named_config_get(maps, "ipv4only-enable", &obj); + } + if (view->rdclass == dns_rdataclass_in && (obj != NULL) + ? cfg_obj_asboolean(obj) + : !ISC_LIST_EMPTY(view->dns64)) + { + const char *server, *contact; + dns_fixedname_t fixed; + dns_name_t *name; + struct { + const char *name; + const char *type; + } zones[] = { + { "ipv4only.arpa", "ipv4only" }, + { "170.0.0.192.in-addr.arpa", "ipv4reverse" }, + { "171.0.0.192.in-addr.arpa", "ipv4reverse" }, + }; + size_t ipv4only_zone; + + obj = NULL; + result = named_config_get(maps, "ipv4only-server", &obj); + if (result == ISC_R_SUCCESS) { + server = cfg_obj_asstring(obj); + } else { + server = NULL; + } + + obj = NULL; + result = named_config_get(maps, "ipv4only-contact", &obj); + if (result == ISC_R_SUCCESS) { + contact = cfg_obj_asstring(obj); + } else { + contact = NULL; + } + + name = dns_fixedname_initname(&fixed); + for (ipv4only_zone = 0; ipv4only_zone < ARRAY_SIZE(zones); + ipv4only_zone++) + { + dns_forwarders_t *dnsforwarders = NULL; + + CHECK(dns_name_fromstring( + name, zones[ipv4only_zone].name, 0, NULL)); + + (void)dns_view_findzone(view, name, &zone); + if (zone != NULL) { + dns_zone_detach(&zone); + continue; + } + + /* + * If we would forward this name don't add it. + */ + result = dns_fwdtable_find(view->fwdtable, name, NULL, + &dnsforwarders); + if ((result == ISC_R_SUCCESS || + result == DNS_R_PARTIALMATCH) && + dnsforwarders->fwdpolicy == dns_fwdpolicy_only) + { + continue; + } + + /* + * See if we can re-use a existing zone. + */ + result = dns_viewlist_find(&named_g_server->viewlist, + view->name, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) + { + goto cleanup; + } + + if (pview != NULL) { + (void)dns_view_findzone(pview, name, &zone); + dns_view_detach(&pview); + } + + CHECK(create_ipv4only_zone(zone, view, name, + zones[ipv4only_zone].type, + mctx, server, contact)); + if (zone != NULL) { + dns_zone_detach(&zone); + } + } + } + + obj = NULL; + result = named_config_get(maps, "rate-limit", &obj); + if (result == ISC_R_SUCCESS) { + result = configure_rrl(view, config, obj); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + + /* + * Set the servfail-ttl. + */ + obj = NULL; + result = named_config_get(maps, "servfail-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + fail_ttl = cfg_obj_asduration(obj); + if (fail_ttl > 30) { + fail_ttl = 30; + } + dns_view_setfailttl(view, fail_ttl); + + /* + * Name space to look up redirect information in. + */ + obj = NULL; + result = named_config_get(maps, "nxdomain-redirect", &obj); + if (result == ISC_R_SUCCESS) { + dns_name_t *name = dns_fixedname_name(&view->redirectfixed); + CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj), 0, + NULL)); + view->redirectzone = name; + } else { + view->redirectzone = NULL; + } + + /* + * Exceptions to DNSSEC validation. + */ + obj = NULL; + result = named_config_get(maps, "validate-except", &obj); + if (result == ISC_R_SUCCESS) { + result = dns_view_getntatable(view, &ntatable); + } + if (result == ISC_R_SUCCESS) { + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + dns_fixedname_t fntaname; + dns_name_t *ntaname; + + ntaname = dns_fixedname_initname(&fntaname); + obj = cfg_listelt_value(element); + CHECK(dns_name_fromstring( + ntaname, cfg_obj_asstring(obj), 0, NULL)); + CHECK(dns_ntatable_add(ntatable, ntaname, true, 0, + 0xffffffffU)); + } + } + +#ifdef HAVE_DNSTAP + /* + * Set up the dnstap environment and configure message + * types to log. + */ + CHECK(configure_dnstap(maps, view)); +#endif /* HAVE_DNSTAP */ + + result = ISC_R_SUCCESS; + +cleanup: + /* + * Revert to the old view if there was an error. + */ + if (result != ISC_R_SUCCESS) { + isc_result_t result2; + + result2 = dns_viewlist_find(&named_g_server->viewlist, + view->name, view->rdclass, &pview); + if (result2 == ISC_R_SUCCESS) { + dns_view_thaw(pview); + + obj = NULL; + if (rpz_configured && + pview->rdclass == dns_rdataclass_in && need_hints && + named_config_get(maps, "response-policy", &obj) == + ISC_R_SUCCESS) + { + /* + * We are swapping the places of the `view` and + * `pview` in the function's parameters list + * because we are reverting the same operation + * done previously in the "correct" order. + */ + result2 = configure_rpz(pview, view, maps, obj, + &old_rpz_ok); + if (result2 != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + ISC_LOG_ERROR, + "rpz configuration " + "revert failed for view " + "'%s'", + pview->name); + } + } + + obj = NULL; + if (catz_configured && + pview->rdclass == dns_rdataclass_in && need_hints && + named_config_get(maps, "catalog-zones", &obj) == + ISC_R_SUCCESS) + { + /* + * We are swapping the places of the `view` and + * `pview` in the function's parameters list + * because we are reverting the same operation + * done previously in the "correct" order. + */ + result2 = configure_catz(pview, view, config, + obj); + if (result2 != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + ISC_LOG_ERROR, + "catz configuration " + "revert failed for view " + "'%s'", + pview->name); + } + } + + dns_view_freeze(pview); + } + + if (pview != NULL) { + dns_view_detach(&pview); + } + + if (zone_element_latest != NULL) { + for (element = cfg_list_first(zonelist); + element != NULL; element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig = + cfg_listelt_value(element); + configure_zone_setviewcommit(result, zconfig, + view); + if (element == zone_element_latest) { + /* + * This was the latest element that was + * successfully configured earlier. + */ + break; + } + } + } + } + + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + if (clients != NULL) { + dns_acl_detach(&clients); + } + if (mapped != NULL) { + dns_acl_detach(&mapped); + } + if (excluded != NULL) { + dns_acl_detach(&excluded); + } + if (ring != NULL) { + dns_tsigkeyring_detach(&ring); + } + if (zone != NULL) { + dns_zone_detach(&zone); + } + if (dispatch4 != NULL) { + dns_dispatch_detach(&dispatch4); + } + if (dispatch6 != NULL) { + dns_dispatch_detach(&dispatch6); + } + if (resstats != NULL) { + isc_stats_detach(&resstats); + } + if (resquerystats != NULL) { + dns_stats_detach(&resquerystats); + } + if (order != NULL) { + dns_order_detach(&order); + } + if (cmctx != NULL) { + isc_mem_detach(&cmctx); + } + if (hmctx != NULL) { + isc_mem_detach(&hmctx); + } + if (cache != NULL) { + dns_cache_detach(&cache); + } + if (dctx != NULL) { + dns_dyndb_destroyctx(&dctx); + } + + return (result); +} + +static isc_result_t +configure_hints(dns_view_t *view, const char *filename) { + isc_result_t result; + dns_db_t *db; + + db = NULL; + result = dns_rootns_create(view->mctx, view->rdclass, filename, &db); + if (result == ISC_R_SUCCESS) { + dns_view_sethints(view, db); + dns_db_detach(&db); + } + + return (result); +} + +static isc_result_t +configure_alternates(const cfg_obj_t *config, dns_view_t *view, + const cfg_obj_t *alternates) { + const cfg_obj_t *portobj; + const cfg_obj_t *addresses; + const cfg_listelt_t *element; + isc_result_t result = ISC_R_SUCCESS; + in_port_t port; + + /* + * Determine which port to send requests to. + */ + CHECKM(named_config_getport(config, "port", &port), "port"); + + if (alternates != NULL) { + portobj = cfg_tuple_get(alternates, "port"); + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, + ISC_LOG_ERROR, + "port '%u' out of range", val); + return (ISC_R_RANGE); + } + port = (in_port_t)val; + } + } + + addresses = NULL; + if (alternates != NULL) { + addresses = cfg_tuple_get(alternates, "addresses"); + } + + for (element = cfg_list_first(addresses); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *alternate = cfg_listelt_value(element); + isc_sockaddr_t sa; + + if (!cfg_obj_issockaddr(alternate)) { + dns_fixedname_t fixed; + dns_name_t *name; + const char *str = cfg_obj_asstring( + cfg_tuple_get(alternate, "name")); + isc_buffer_t buffer; + in_port_t myport = port; + + isc_buffer_constinit(&buffer, str, strlen(str)); + isc_buffer_add(&buffer, strlen(str)); + name = dns_fixedname_initname(&fixed); + CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0, + NULL)); + + portobj = cfg_tuple_get(alternate, "port"); + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, + ISC_LOG_ERROR, + "port '%u' out of range", + val); + return (ISC_R_RANGE); + } + myport = (in_port_t)val; + } + dns_resolver_addalternate(view->resolver, NULL, name, + myport); + continue; + } + + sa = *cfg_obj_assockaddr(alternate); + if (isc_sockaddr_getport(&sa) == 0) { + isc_sockaddr_setport(&sa, port); + } + dns_resolver_addalternate(view->resolver, &sa, NULL, 0); + } + +cleanup: + return (result); +} + +static isc_result_t +configure_forward(const cfg_obj_t *config, dns_view_t *view, + const dns_name_t *origin, const cfg_obj_t *forwarders, + const cfg_obj_t *forwardtype) { + const cfg_obj_t *portobj = NULL; + const cfg_obj_t *faddresses = NULL; + const cfg_listelt_t *element = NULL; + dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none; + dns_forwarderlist_t fwdlist; + dns_forwarder_t *fwd = NULL; + isc_result_t result; + in_port_t port; + + ISC_LIST_INIT(fwdlist); + + /* + * Determine which port to send forwarded requests to. + */ + CHECKM(named_config_getport(config, "port", &port), "port"); + + if (forwarders != NULL) { + portobj = cfg_tuple_get(forwarders, "port"); + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, named_g_lctx, + ISC_LOG_ERROR, + "port '%u' out of range", val); + return (ISC_R_RANGE); + } + port = (in_port_t)val; + } + } + + faddresses = NULL; + if (forwarders != NULL) { + faddresses = cfg_tuple_get(forwarders, "addresses"); + } + + for (element = cfg_list_first(faddresses); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *forwarder = cfg_listelt_value(element); + fwd = isc_mem_get(view->mctx, sizeof(dns_forwarder_t)); + fwd->addr = *cfg_obj_assockaddr(forwarder); + if (isc_sockaddr_getport(&fwd->addr) == 0) { + isc_sockaddr_setport(&fwd->addr, port); + } + ISC_LINK_INIT(fwd, link); + ISC_LIST_APPEND(fwdlist, fwd, link); + } + + if (ISC_LIST_EMPTY(fwdlist)) { + if (forwardtype != NULL) { + cfg_obj_log(forwardtype, named_g_lctx, ISC_LOG_WARNING, + "no forwarders seen; disabling " + "forwarding"); + } + fwdpolicy = dns_fwdpolicy_none; + } else { + if (forwardtype == NULL) { + fwdpolicy = dns_fwdpolicy_first; + } else { + const char *forwardstr = cfg_obj_asstring(forwardtype); + if (strcasecmp(forwardstr, "first") == 0) { + fwdpolicy = dns_fwdpolicy_first; + } else if (strcasecmp(forwardstr, "only") == 0) { + fwdpolicy = dns_fwdpolicy_only; + } else { + UNREACHABLE(); + } + } + } + + result = dns_fwdtable_addfwd(view->fwdtable, origin, &fwdlist, + fwdpolicy); + if (result != ISC_R_SUCCESS) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(origin, namebuf, sizeof(namebuf)); + cfg_obj_log(forwarders, named_g_lctx, ISC_LOG_WARNING, + "could not set up forwarding for domain '%s': %s", + namebuf, isc_result_totext(result)); + goto cleanup; + } + + if (fwdpolicy == dns_fwdpolicy_only) { + dns_view_sfd_add(view, origin); + } + + result = ISC_R_SUCCESS; + +cleanup: + + while (!ISC_LIST_EMPTY(fwdlist)) { + fwd = ISC_LIST_HEAD(fwdlist); + ISC_LIST_UNLINK(fwdlist, fwd, link); + isc_mem_put(view->mctx, fwd, sizeof(dns_forwarder_t)); + } + + return (result); +} + +static isc_result_t +get_viewinfo(const cfg_obj_t *vconfig, const char **namep, + dns_rdataclass_t *classp) { + isc_result_t result = ISC_R_SUCCESS; + const char *viewname; + dns_rdataclass_t viewclass; + + REQUIRE(namep != NULL && *namep == NULL); + REQUIRE(classp != NULL); + + if (vconfig != NULL) { + const cfg_obj_t *classobj = NULL; + + viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name")); + classobj = cfg_tuple_get(vconfig, "class"); + CHECK(named_config_getclass(classobj, dns_rdataclass_in, + &viewclass)); + if (dns_rdataclass_ismeta(viewclass)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "view '%s': class must not be meta", + viewname); + CHECK(ISC_R_FAILURE); + } + } else { + viewname = "_default"; + viewclass = dns_rdataclass_in; + } + + *namep = viewname; + *classp = viewclass; + +cleanup: + return (result); +} + +/* + * Find a view based on its configuration info and attach to it. + * + * If 'vconfig' is NULL, attach to the default view. + */ +static isc_result_t +find_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, + dns_view_t **viewp) { + isc_result_t result; + const char *viewname = NULL; + dns_rdataclass_t viewclass; + dns_view_t *view = NULL; + + result = get_viewinfo(vconfig, &viewname, &viewclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = dns_viewlist_find(viewlist, viewname, viewclass, &view); + if (result != ISC_R_SUCCESS) { + return (result); + } + + *viewp = view; + return (ISC_R_SUCCESS); +} + +/* + * Create a new view and add it to the list. + * + * If 'vconfig' is NULL, create the default view. + * + * The view created is attached to '*viewp'. + */ +static isc_result_t +create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist, + dns_view_t **viewp) { + isc_result_t result; + const char *viewname = NULL; + dns_rdataclass_t viewclass; + dns_view_t *view = NULL; + + result = get_viewinfo(vconfig, &viewname, &viewclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = dns_viewlist_find(viewlist, viewname, viewclass, &view); + if (result == ISC_R_SUCCESS) { + return (ISC_R_EXISTS); + } + if (result != ISC_R_NOTFOUND) { + return (result); + } + INSIST(view == NULL); + + result = dns_view_create(named_g_mctx, viewclass, viewname, &view); + if (result != ISC_R_SUCCESS) { + return (result); + } + + isc_nonce_buf(view->secret, sizeof(view->secret)); + + ISC_LIST_APPEND(*viewlist, view, link); + dns_view_attach(view, viewp); + return (ISC_R_SUCCESS); +} + +/* + * Configure or reconfigure a zone. + */ +static isc_result_t +configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, + const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, + dns_viewlist_t *viewlist, dns_kasplist_t *kasplist, + cfg_aclconfctx_t *aclconf, bool added, bool old_rpz_ok, + bool modify) { + dns_view_t *pview = NULL; /* Production view */ + dns_zone_t *zone = NULL; /* New or reused zone */ + dns_zone_t *raw = NULL; /* New or reused raw zone */ + dns_zone_t *dupzone = NULL; + const cfg_obj_t *options = NULL; + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *typeobj = NULL; + const cfg_obj_t *forwarders = NULL; + const cfg_obj_t *forwardtype = NULL; + const cfg_obj_t *ixfrfromdiffs = NULL; + const cfg_obj_t *only = NULL; + const cfg_obj_t *viewobj = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_buffer_t buffer; + dns_fixedname_t fixorigin; + dns_name_t *origin; + const char *zname; + dns_rdataclass_t zclass; + const char *ztypestr; + dns_rpz_num_t rpz_num; + bool zone_is_catz = false; + bool zone_maybe_inline = false; + bool inline_signing = false; + bool fullsign = false; + + options = NULL; + (void)cfg_map_get(config, "options", &options); + + zoptions = cfg_tuple_get(zconfig, "options"); + + /* + * Get the zone origin as a dns_name_t. + */ + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + isc_buffer_constinit(&buffer, zname, strlen(zname)); + isc_buffer_add(&buffer, strlen(zname)); + dns_fixedname_init(&fixorigin); + CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer, + dns_rootname, 0, NULL)); + origin = dns_fixedname_name(&fixorigin); + + CHECK(named_config_getclass(cfg_tuple_get(zconfig, "class"), + view->rdclass, &zclass)); + if (zclass != view->rdclass) { + const char *vname = NULL; + if (vconfig != NULL) { + vname = cfg_obj_asstring( + cfg_tuple_get(vconfig, "name")); + } else { + vname = ""; + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': wrong class for view '%s'", zname, + vname); + result = ISC_R_FAILURE; + goto cleanup; + } + + (void)cfg_map_get(zoptions, "in-view", &viewobj); + if (viewobj != NULL) { + const char *inview = cfg_obj_asstring(viewobj); + dns_view_t *otherview = NULL; + + if (viewlist == NULL) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "'in-view' option is not permitted in " + "dynamically added zones"); + result = ISC_R_FAILURE; + goto cleanup; + } + + result = dns_viewlist_find(viewlist, inview, view->rdclass, + &otherview); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "view '%s' is not yet defined.", inview); + result = ISC_R_FAILURE; + goto cleanup; + } + + result = dns_view_findzone(otherview, origin, &zone); + dns_view_detach(&otherview); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "zone '%s' not defined in view '%s'", zname, + inview); + result = ISC_R_FAILURE; + goto cleanup; + } + + CHECK(dns_view_addzone(view, zone)); + dns_zone_detach(&zone); + + /* + * If the zone contains a 'forwarders' statement, configure + * selective forwarding. Note: this is not inherited from the + * other view. + */ + forwarders = NULL; + result = cfg_map_get(zoptions, "forwarders", &forwarders); + if (result == ISC_R_SUCCESS) { + forwardtype = NULL; + (void)cfg_map_get(zoptions, "forward", &forwardtype); + CHECK(configure_forward(config, view, origin, + forwarders, forwardtype)); + } + result = ISC_R_SUCCESS; + goto cleanup; + } + + (void)cfg_map_get(zoptions, "type", &typeobj); + if (typeobj == NULL) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "zone '%s' 'type' not specified", zname); + result = ISC_R_FAILURE; + goto cleanup; + } + ztypestr = cfg_obj_asstring(typeobj); + + /* + * "hints zones" aren't zones. If we've got one, + * configure it and return. + */ + if (strcasecmp(ztypestr, "hint") == 0) { + const cfg_obj_t *fileobj = NULL; + if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': 'file' not specified", zname); + result = ISC_R_FAILURE; + goto cleanup; + } + if (dns_name_equal(origin, dns_rootname)) { + const char *hintsfile = cfg_obj_asstring(fileobj); + + CHECK(configure_hints(view, hintsfile)); + + /* + * Hint zones may also refer to delegation only points. + */ + only = NULL; + tresult = cfg_map_get(zoptions, "delegation-only", + &only); + if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) + { + dns_view_adddelegationonly(view, origin); + } + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "ignoring non-root hint zone '%s'", + zname); + result = ISC_R_SUCCESS; + } + /* Skip ordinary zone processing. */ + goto cleanup; + } + + /* + * "forward zones" aren't zones either. Translate this syntax into + * the appropriate selective forwarding configuration and return. + */ + if (strcasecmp(ztypestr, "forward") == 0) { + forwardtype = NULL; + forwarders = NULL; + + (void)cfg_map_get(zoptions, "forward", &forwardtype); + (void)cfg_map_get(zoptions, "forwarders", &forwarders); + CHECK(configure_forward(config, view, origin, forwarders, + forwardtype)); + + /* + * Forward zones may also set delegation only. + */ + only = NULL; + tresult = cfg_map_get(zoptions, "delegation-only", &only); + if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only)) { + dns_view_adddelegationonly(view, origin); + } + goto cleanup; + } + + /* + * "delegation-only zones" aren't zones either. + */ + if (strcasecmp(ztypestr, "delegation-only") == 0) { + dns_view_adddelegationonly(view, origin); + goto cleanup; + } + + /* + * Redirect zones only require minimal configuration. + */ + if (strcasecmp(ztypestr, "redirect") == 0) { + if (view->redirect != NULL) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "redirect zone already exists"); + result = ISC_R_EXISTS; + goto cleanup; + } + result = dns_viewlist_find(viewlist, view->name, view->rdclass, + &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { + goto cleanup; + } + if (pview != NULL && pview->redirect != NULL) { + dns_zone_attach(pview->redirect, &zone); + dns_zone_setview(zone, view); + } else { + CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, + &zone)); + CHECK(dns_zone_setorigin(zone, origin)); + dns_zone_setview(zone, view); + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, + zone)); + dns_zone_setstats(zone, named_g_server->zonestats); + } + CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, + kasplist, zone, NULL)); + dns_zone_attach(zone, &view->redirect); + goto cleanup; + } + + if (!modify) { + /* + * Check for duplicates in the new zone table. + */ + result = dns_view_findzone(view, origin, &dupzone); + if (result == ISC_R_SUCCESS) { + /* + * We already have this zone! + */ + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "zone '%s' already exists", zname); + dns_zone_detach(&dupzone); + result = ISC_R_EXISTS; + goto cleanup; + } + INSIST(dupzone == NULL); + } + + /* + * Note whether this is a response policy zone and which one if so, + * unless we are using RPZ service interface. In that case, the + * BIND zone database has nothing to do with rpz and so we don't care. + */ + for (rpz_num = 0;; ++rpz_num) { + if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones || + view->rpzs->p.dnsrps_enabled) + { + rpz_num = DNS_RPZ_INVALID_NUM; + break; + } + if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, origin)) + { + break; + } + } + + if (view->catzs != NULL && + dns_catz_get_zone(view->catzs, origin) != NULL) + { + zone_is_catz = true; + } + + /* + * See if we can reuse an existing zone. This is + * only possible if all of these are true: + * - The zone's view exists + * - A zone with the right name exists in the view + * - The zone is compatible with the config + * options (e.g., an existing primary zone cannot + * be reused if the options specify a secondary zone) + * - The zone was not and is still not a response policy zone + * or the zone is a policy zone with an unchanged number + * and we are using the old policy zone summary data. + */ + result = dns_viewlist_find(&named_g_server->viewlist, view->name, + view->rdclass, &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { + goto cleanup; + } + if (pview != NULL) { + result = dns_view_findzone(pview, origin, &zone); + } + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (zone != NULL && !named_zone_reusable(zone, zconfig)) { + dns_zone_detach(&zone); + fullsign = true; + } + + if (zone != NULL && (rpz_num != dns_zone_get_rpz_num(zone) || + (rpz_num != DNS_RPZ_INVALID_NUM && !old_rpz_ok))) + { + dns_zone_detach(&zone); + } + + if (zone != NULL) { + /* + * We found a reusable zone. Make it use the + * new view. + */ + dns_zone_setview(zone, view); + } else { + /* + * We cannot reuse an existing zone, we have + * to create a new one. + */ + CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone)); + CHECK(dns_zone_setorigin(zone, origin)); + dns_zone_setview(zone, view); + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone)); + dns_zone_setstats(zone, named_g_server->zonestats); + } + if (rpz_num != DNS_RPZ_INVALID_NUM) { + result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': incompatible" + " masterfile-format or database" + " for a response policy zone", + zname); + goto cleanup; + } + } + + if (zone_is_catz) { + dns_zone_catz_enable(zone, view->catzs); + } else if (dns_zone_catz_is_enabled(zone)) { + dns_zone_catz_disable(zone); + } + + /* + * If the zone contains a 'forwarders' statement, configure + * selective forwarding. + */ + forwarders = NULL; + if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) { + forwardtype = NULL; + (void)cfg_map_get(zoptions, "forward", &forwardtype); + CHECK(configure_forward(config, view, origin, forwarders, + forwardtype)); + } + + /* + * Stub and forward zones may also refer to delegation only points. + */ + only = NULL; + if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS) { + if (cfg_obj_asboolean(only)) { + dns_view_adddelegationonly(view, origin); + } + } + + /* + * Mark whether the zone was originally added at runtime or not + */ + dns_zone_setadded(zone, added); + + /* + * Determine if we need to set up inline signing. + */ + zone_maybe_inline = ((strcasecmp(ztypestr, "primary") == 0 || + strcasecmp(ztypestr, "master") == 0 || + strcasecmp(ztypestr, "secondary") == 0 || + strcasecmp(ztypestr, "slave") == 0)); + + if (zone_maybe_inline) { + inline_signing = named_zone_inlinesigning(zconfig); + } + if (inline_signing) { + dns_zone_getraw(zone, &raw); + if (raw == NULL) { + CHECK(dns_zone_create(&raw, mctx)); + CHECK(dns_zone_setorigin(raw, origin)); + dns_zone_setview(raw, view); + dns_zone_setstats(raw, named_g_server->zonestats); + CHECK(dns_zone_link(zone, raw)); + } + if (cfg_map_get(zoptions, "ixfr-from-differences", + &ixfrfromdiffs) == ISC_R_SUCCESS) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "zone '%s': 'ixfr-from-differences' is " + "ignored for inline-signed zones", + zname); + } + } + + /* + * Configure the zone. + */ + CHECK(named_zone_configure(config, vconfig, zconfig, aclconf, kasplist, + zone, raw)); + + /* + * Add the zone to its view in the new view list. + */ + if (!modify) { + CHECK(dns_view_addzone(view, zone)); + } + + if (zone_is_catz) { + /* + * force catz reload if the zone is loaded; + * if it's not it'll get reloaded on zone load + */ + dns_db_t *db = NULL; + + tresult = dns_zone_getdb(zone, &db); + if (tresult == ISC_R_SUCCESS) { + dns_catz_dbupdate_callback(db, view->catzs); + dns_db_detach(&db); + } + } + + /* + * Ensure that zone keys are reloaded on reconfig + */ + if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0) { + dns_zone_rekey(zone, fullsign); + } + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + if (raw != NULL) { + dns_zone_detach(&raw); + } + if (pview != NULL) { + dns_view_detach(&pview); + } + + return (result); +} + +/* + * Configure built-in zone for storing managed-key data. + */ +static isc_result_t +add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) { + isc_result_t result; + dns_view_t *pview = NULL; + dns_zone_t *zone = NULL; + dns_acl_t *none = NULL; + char filename[PATH_MAX]; + bool defaultview; + + REQUIRE(view != NULL); + + /* See if we can re-use an existing keydata zone. */ + result = dns_viewlist_find(&named_g_server->viewlist, view->name, + view->rdclass, &pview); + if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { + return (result); + } + + if (pview != NULL) { + if (pview->managed_keys != NULL) { + dns_zone_attach(pview->managed_keys, + &view->managed_keys); + dns_zone_setview(pview->managed_keys, view); + dns_zone_setviewcommit(pview->managed_keys); + dns_view_detach(&pview); + dns_zone_synckeyzone(view->managed_keys); + return (ISC_R_SUCCESS); + } + + dns_view_detach(&pview); + } + + /* No existing keydata zone was found; create one */ + CHECK(dns_zonemgr_createzone(named_g_server->zonemgr, &zone)); + CHECK(dns_zone_setorigin(zone, dns_rootname)); + + defaultview = (strcmp(view->name, "_default") == 0); + CHECK(isc_file_sanitize( + directory, defaultview ? "managed-keys" : view->name, + defaultview ? "bind" : "mkeys", filename, sizeof(filename))); + CHECK(dns_zone_setfile(zone, filename, dns_masterformat_text, + &dns_master_style_default)); + + dns_zone_setview(zone, view); + dns_zone_settype(zone, dns_zone_key); + dns_zone_setclass(zone, view->rdclass); + + CHECK(dns_zonemgr_managezone(named_g_server->zonemgr, zone)); + + CHECK(dns_acl_none(mctx, &none)); + dns_zone_setqueryacl(zone, none); + dns_zone_setqueryonacl(zone, none); + dns_acl_detach(&none); + + dns_zone_setdialup(zone, dns_dialuptype_no); + dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); + dns_zone_setjournalsize(zone, 0); + + dns_zone_setstats(zone, named_g_server->zonestats); + CHECK(setquerystats(zone, mctx, dns_zonestat_none)); + + if (view->managed_keys != NULL) { + dns_zone_detach(&view->managed_keys); + } + dns_zone_attach(zone, &view->managed_keys); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "set up managed keys zone for view %s, file '%s'", + view->name, filename); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + if (none != NULL) { + dns_acl_detach(&none); + } + + return (result); +} + +/* + * Configure a single server quota. + */ +static void +configure_server_quota(const cfg_obj_t **maps, const char *name, + isc_quota_t *quota) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = named_config_get(maps, name, &obj); + INSIST(result == ISC_R_SUCCESS); + isc_quota_max(quota, cfg_obj_asuint32(obj)); +} + +/* + * This function is called as soon as the 'directory' statement has been + * parsed. This can be extended to support other options if necessary. + */ +static isc_result_t +directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) { + isc_result_t result; + const char *directory; + + REQUIRE(strcasecmp("directory", clausename) == 0); + + UNUSED(arg); + UNUSED(clausename); + + /* + * Change directory. + */ + directory = cfg_obj_asstring(obj); + + if (!isc_file_ischdiridempotent(directory)) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "option 'directory' contains relative path '%s'", + directory); + } + + if (!isc_file_isdirwritable(directory)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "directory '%s' is not writable", directory); + return (ISC_R_NOPERM); + } + + result = isc_dir_chdir(directory); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "change directory to '%s' failed: %s", directory, + isc_result_totext(result)); + return (result); + } + + return (ISC_R_SUCCESS); +} + +/* + * This event callback is invoked to do periodic network interface + * scanning. + */ + +static void +interface_timer_tick(isc_task_t *task, isc_event_t *event) { + named_server_t *server = (named_server_t *)event->ev_arg; + INSIST(task == server->task); + UNUSED(task); + + isc_event_free(&event); + ns_interfacemgr_scan(server->interfacemgr, false, false); +} + +static void +heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) { + named_server_t *server = (named_server_t *)event->ev_arg; + dns_view_t *view; + + UNUSED(task); + isc_event_free(&event); + view = ISC_LIST_HEAD(server->viewlist); + while (view != NULL) { + dns_view_dialup(view); + view = ISC_LIST_NEXT(view, link); + } +} + +typedef struct { + isc_mem_t *mctx; + isc_task_t *task; + dns_fetch_t *fetch; + dns_view_t *view; + dns_fixedname_t tatname; + dns_fixedname_t keyname; + dns_rdataset_t rdataset; + dns_rdataset_t sigrdataset; +} ns_tat_t; + +static int +cid(const void *a, const void *b) { + const uint16_t ida = *(const uint16_t *)a; + const uint16_t idb = *(const uint16_t *)b; + if (ida < idb) { + return (-1); + } else if (ida > idb) { + return (1); + } else { + return (0); + } +} + +static void +tat_done(isc_task_t *task, isc_event_t *event) { + dns_fetchevent_t *devent; + ns_tat_t *tat; + + INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE); + INSIST(event->ev_arg != NULL); + + UNUSED(task); + + tat = event->ev_arg; + devent = (dns_fetchevent_t *)event; + + /* Free resources which are not of interest */ + if (devent->node != NULL) { + dns_db_detachnode(devent->db, &devent->node); + } + if (devent->db != NULL) { + dns_db_detach(&devent->db); + } + isc_event_free(&event); + dns_resolver_destroyfetch(&tat->fetch); + if (dns_rdataset_isassociated(&tat->rdataset)) { + dns_rdataset_disassociate(&tat->rdataset); + } + if (dns_rdataset_isassociated(&tat->sigrdataset)) { + dns_rdataset_disassociate(&tat->sigrdataset); + } + dns_view_detach(&tat->view); + isc_task_detach(&tat->task); + isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat)); +} + +struct dotat_arg { + dns_view_t *view; + isc_task_t *task; +}; + +/*% + * Prepare the QNAME for the TAT query to be sent by processing the trust + * anchors present at 'keynode' of 'keytable'. Store the result in 'dst' and + * the domain name which 'keynode' is associated with in 'origin'. + * + * A maximum of 12 key IDs can be reported in a single TAT query due to the + * 63-octet length limit for any single label in a domain name. If there are + * more than 12 keys configured at 'keynode', only the first 12 will be + * reported in the TAT query. + */ +static isc_result_t +get_tat_qname(dns_name_t *target, dns_name_t *keyname, dns_keynode_t *keynode) { + dns_rdataset_t dsset; + unsigned int i, n = 0; + uint16_t ids[12]; + isc_textregion_t r; + char label[64]; + int m; + + dns_rdataset_init(&dsset); + if (dns_keynode_dsset(keynode, &dsset)) { + isc_result_t result; + + for (result = dns_rdataset_first(&dsset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&dsset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_ds_t ds; + + dns_rdata_reset(&rdata); + dns_rdataset_current(&dsset, &rdata); + result = dns_rdata_tostruct(&rdata, &ds, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (n < (sizeof(ids) / sizeof(ids[0]))) { + ids[n] = ds.key_tag; + n++; + } + } + dns_rdataset_disassociate(&dsset); + } + + if (n == 0) { + return (DNS_R_EMPTYNAME); + } + + if (n > 1) { + qsort(ids, n, sizeof(ids[0]), cid); + } + + /* + * Encoded as "_ta-xxxx\(-xxxx\)*" where xxxx is the hex version of + * of the keyid. + */ + label[0] = 0; + r.base = label; + r.length = sizeof(label); + m = snprintf(r.base, r.length, "_ta"); + if (m < 0 || (unsigned)m > r.length) { + return (ISC_R_FAILURE); + } + isc_textregion_consume(&r, m); + for (i = 0; i < n; i++) { + m = snprintf(r.base, r.length, "-%04x", ids[i]); + if (m < 0 || (unsigned)m > r.length) { + return (ISC_R_FAILURE); + } + isc_textregion_consume(&r, m); + } + + return (dns_name_fromstring2(target, label, keyname, 0, NULL)); +} + +static void +tat_send(isc_task_t *task, isc_event_t *event) { + ns_tat_t *tat; + char namebuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fdomain; + dns_name_t *domain; + dns_rdataset_t nameservers; + isc_result_t result; + dns_name_t *keyname; + dns_name_t *tatname; + + INSIST(event != NULL && event->ev_type == NAMED_EVENT_TATSEND); + INSIST(event->ev_arg != NULL); + + UNUSED(task); + + tat = event->ev_arg; + + keyname = dns_fixedname_name(&tat->keyname); + tatname = dns_fixedname_name(&tat->tatname); + + dns_name_format(tatname, namebuf, sizeof(namebuf)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "%s: sending trust-anchor-telemetry query '%s/NULL'", + tat->view->name, namebuf); + + /* + * TAT queries should be sent to the authoritative servers for a given + * zone. If this function is called for a keytable node corresponding + * to a locally served zone, calling dns_resolver_createfetch() with + * NULL 'domain' and 'nameservers' arguments will cause 'tatname' to be + * resolved locally, without sending any TAT queries upstream. + * + * Work around this issue by calling dns_view_findzonecut() first. If + * the zone is served locally, the NS RRset for the given domain name + * will be retrieved from local data; if it is not, the deepest zone + * cut we have for it will be retrieved from cache. In either case, + * passing the results to dns_resolver_createfetch() will prevent it + * from returning NXDOMAIN for 'tatname' while still allowing it to + * chase down any potential delegations returned by upstream servers in + * order to eventually find the destination host to send the TAT query + * to. + * + * After the dns_view_findzonecut() call, 'domain' will hold the + * deepest zone cut we can find for 'keyname' while 'nameservers' will + * hold the NS RRset at that zone cut. + */ + domain = dns_fixedname_initname(&fdomain); + dns_rdataset_init(&nameservers); + result = dns_view_findzonecut(tat->view, keyname, domain, NULL, 0, 0, + true, true, &nameservers, NULL); + if (result == ISC_R_SUCCESS) { + result = dns_resolver_createfetch( + tat->view->resolver, tatname, dns_rdatatype_null, + domain, &nameservers, NULL, NULL, 0, 0, 0, NULL, + tat->task, tat_done, tat, &tat->rdataset, + &tat->sigrdataset, &tat->fetch); + } + + /* + * 'domain' holds the dns_name_t pointer inside a dst_key_t structure. + * dns_resolver_createfetch() creates its own copy of 'domain' if it + * succeeds. Thus, 'domain' is not freed here. + * + * Even if dns_view_findzonecut() returned something else than + * ISC_R_SUCCESS, it still could have associated 'nameservers'. + * dns_resolver_createfetch() creates its own copy of 'nameservers' if + * it succeeds. Thus, we need to check whether 'nameservers' is + * associated and release it if it is. + */ + if (dns_rdataset_isassociated(&nameservers)) { + dns_rdataset_disassociate(&nameservers); + } + + if (result != ISC_R_SUCCESS) { + dns_view_detach(&tat->view); + isc_task_detach(&tat->task); + isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat)); + } + isc_event_free(&event); +} + +static void +dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, dns_name_t *keyname, + void *arg) { + struct dotat_arg *dotat_arg = arg; + isc_result_t result; + dns_view_t *view; + isc_task_t *task; + ns_tat_t *tat; + isc_event_t *event; + + REQUIRE(keytable != NULL); + REQUIRE(keynode != NULL); + REQUIRE(dotat_arg != NULL); + + view = dotat_arg->view; + task = dotat_arg->task; + + tat = isc_mem_get(dotat_arg->view->mctx, sizeof(*tat)); + + tat->fetch = NULL; + tat->mctx = NULL; + tat->task = NULL; + tat->view = NULL; + dns_rdataset_init(&tat->rdataset); + dns_rdataset_init(&tat->sigrdataset); + dns_name_copy(keyname, dns_fixedname_initname(&tat->keyname)); + result = get_tat_qname(dns_fixedname_initname(&tat->tatname), keyname, + keynode); + if (result != ISC_R_SUCCESS) { + isc_mem_put(dotat_arg->view->mctx, tat, sizeof(*tat)); + return; + } + isc_mem_attach(dotat_arg->view->mctx, &tat->mctx); + isc_task_attach(task, &tat->task); + dns_view_attach(view, &tat->view); + + /* + * We don't want to be holding the keytable lock when calling + * dns_view_findzonecut() as it creates a lock order loop so + * call dns_view_findzonecut() in a event handler. + * + * zone->lock (dns_zone_setviewcommit) while holding view->lock + * (dns_view_setviewcommit) + * + * keytable->lock (dns_keytable_find) while holding zone->lock + * (zone_asyncload) + * + * view->lock (dns_view_findzonecut) while holding keytable->lock + * (dns_keytable_forall) + */ + event = isc_event_allocate(tat->mctx, keytable, NAMED_EVENT_TATSEND, + tat_send, tat, sizeof(isc_event_t)); + isc_task_send(task, &event); +} + +static void +tat_timer_tick(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + named_server_t *server = (named_server_t *)event->ev_arg; + struct dotat_arg arg; + dns_view_t *view; + dns_keytable_t *secroots = NULL; + + isc_event_free(&event); + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (!view->trust_anchor_telemetry || !view->enablevalidation) { + continue; + } + + result = dns_view_getsecroots(view, &secroots); + if (result != ISC_R_SUCCESS) { + continue; + } + + arg.view = view; + arg.task = task; + (void)dns_keytable_forall(secroots, dotat, &arg); + dns_keytable_detach(&secroots); + } +} + +static void +pps_timer_tick(isc_task_t *task, isc_event_t *event) { + static unsigned int oldrequests = 0; + unsigned int requests = atomic_load_relaxed(&ns_client_requests); + + UNUSED(task); + isc_event_free(&event); + + /* + * Don't worry about wrapping as the overflow result will be right. + */ + dns_pps = (requests - oldrequests) / 1200; + oldrequests = requests; +} + +/* + * Replace the current value of '*field', a dynamically allocated + * string or NULL, with a dynamically allocated copy of the + * null-terminated string pointed to by 'value', or NULL. + */ +static isc_result_t +setstring(named_server_t *server, char **field, const char *value) { + char *copy; + + if (value != NULL) { + copy = isc_mem_strdup(server->mctx, value); + } else { + copy = NULL; + } + + if (*field != NULL) { + isc_mem_free(server->mctx, *field); + } + + *field = copy; + return (ISC_R_SUCCESS); +} + +/* + * Replace the current value of '*field', a dynamically allocated + * string or NULL, with another dynamically allocated string + * or NULL if whether 'obj' is a string or void value, respectively. + */ +static isc_result_t +setoptstring(named_server_t *server, char **field, const cfg_obj_t *obj) { + if (cfg_obj_isvoid(obj)) { + return (setstring(server, field, NULL)); + } else { + return (setstring(server, field, cfg_obj_asstring(obj))); + } +} + +static void +set_limit(const cfg_obj_t **maps, const char *configname, + const char *description, isc_resource_t resourceid, + isc_resourcevalue_t defaultvalue) { + const cfg_obj_t *obj = NULL; + const char *resource; + isc_resourcevalue_t value; + isc_result_t result; + + if (named_config_get(maps, configname, &obj) != ISC_R_SUCCESS) { + return; + } + + if (cfg_obj_isstring(obj)) { + resource = cfg_obj_asstring(obj); + if (strcasecmp(resource, "unlimited") == 0) { + value = ISC_RESOURCE_UNLIMITED; + } else { + INSIST(strcasecmp(resource, "default") == 0); + value = defaultvalue; + } + } else { + value = cfg_obj_asuint64(obj); + } + + result = isc_resource_setlimit(resourceid, value); + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + result == ISC_R_SUCCESS ? ISC_LOG_DEBUG(3) : ISC_LOG_WARNING, + "set maximum %s to %" PRIu64 ": %s", description, value, + isc_result_totext(result)); +} + +#define SETLIMIT(cfgvar, resource, description) \ + set_limit(maps, cfgvar, description, isc_resource_##resource, \ + named_g_init##resource) + +static void +set_limits(const cfg_obj_t **maps) { + SETLIMIT("stacksize", stacksize, "stack size"); + SETLIMIT("datasize", datasize, "data size"); + SETLIMIT("coresize", coresize, "core size"); + SETLIMIT("files", openfiles, "open files"); +} + +static void +portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports, + bool positive) { + const cfg_listelt_t *element; + + for (element = cfg_list_first(ports); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *obj = cfg_listelt_value(element); + + if (cfg_obj_isuint32(obj)) { + in_port_t port = (in_port_t)cfg_obj_asuint32(obj); + + if (positive) { + isc_portset_add(portset, port); + } else { + isc_portset_remove(portset, port); + } + } else { + const cfg_obj_t *obj_loport, *obj_hiport; + in_port_t loport, hiport; + + obj_loport = cfg_tuple_get(obj, "loport"); + loport = (in_port_t)cfg_obj_asuint32(obj_loport); + obj_hiport = cfg_tuple_get(obj, "hiport"); + hiport = (in_port_t)cfg_obj_asuint32(obj_hiport); + + if (positive) { + isc_portset_addrange(portset, loport, hiport); + } else { + isc_portset_removerange(portset, loport, + hiport); + } + } + } +} + +static isc_result_t +removed(dns_zone_t *zone, void *uap) { + if (dns_zone_getview(zone) != uap) { + return (ISC_R_SUCCESS); + } + + dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", + dns_zonetype_name(dns_zone_gettype(zone))); + return (ISC_R_SUCCESS); +} + +static void +cleanup_session_key(named_server_t *server, isc_mem_t *mctx) { + if (server->session_keyfile != NULL) { + isc_file_remove(server->session_keyfile); + isc_mem_free(mctx, server->session_keyfile); + server->session_keyfile = NULL; + } + + if (server->session_keyname != NULL) { + if (dns_name_dynamic(server->session_keyname)) { + dns_name_free(server->session_keyname, mctx); + } + isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t)); + server->session_keyname = NULL; + } + + if (server->sessionkey != NULL) { + dst_key_free(&server->sessionkey); + } + + server->session_keyalg = DST_ALG_UNKNOWN; + server->session_keybits = 0; +} + +static isc_result_t +generate_session_key(const char *filename, const char *keynamestr, + const dns_name_t *keyname, const char *algstr, + unsigned int algtype, uint16_t bits, isc_mem_t *mctx, + bool first_time, dst_key_t **keyp) { + isc_result_t result = ISC_R_SUCCESS; + dst_key_t *key = NULL; + isc_buffer_t key_txtbuffer; + isc_buffer_t key_rawbuffer; + char key_txtsecret[256]; + char key_rawsecret[64]; + isc_region_t key_rawregion; + FILE *fp = NULL; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "generating session key for dynamic DNS"); + + /* generate key */ + result = dst_key_generate(keyname, algtype, bits, 1, 0, + DNS_KEYPROTO_ANY, dns_rdataclass_in, mctx, + &key, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* + * Dump the key to the buffer for later use. + */ + isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret)); + CHECK(dst_key_tobuffer(key, &key_rawbuffer)); + + isc_buffer_usedregion(&key_rawbuffer, &key_rawregion); + isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); + CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer)); + + /* Dump the key to the key file. */ + fp = named_os_openfile(filename, S_IRUSR | S_IWUSR, first_time); + if (fp == NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "could not create %s", filename); + result = ISC_R_NOPERM; + goto cleanup; + } + + fprintf(fp, + "key \"%s\" {\n" + "\talgorithm %s;\n" + "\tsecret \"%.*s\";\n};\n", + keynamestr, algstr, (int)isc_buffer_usedlength(&key_txtbuffer), + (char *)isc_buffer_base(&key_txtbuffer)); + + CHECK(isc_stdio_flush(fp)); + result = isc_stdio_close(fp); + fp = NULL; + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + *keyp = key; + return (ISC_R_SUCCESS); + +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed to generate session key " + "for dynamic DNS: %s", + isc_result_totext(result)); + if (fp != NULL) { + (void)isc_stdio_close(fp); + (void)isc_file_remove(filename); + } + if (key != NULL) { + dst_key_free(&key); + } + + return (result); +} + +static isc_result_t +configure_session_key(const cfg_obj_t **maps, named_server_t *server, + isc_mem_t *mctx, bool first_time) { + const char *keyfile, *keynamestr, *algstr; + unsigned int algtype; + dns_fixedname_t fname; + dns_name_t *keyname; + const dns_name_t *algname; + isc_buffer_t buffer; + uint16_t bits; + const cfg_obj_t *obj; + bool need_deleteold = false; + bool need_createnew = false; + isc_result_t result; + + obj = NULL; + result = named_config_get(maps, "session-keyfile", &obj); + if (result == ISC_R_SUCCESS) { + if (cfg_obj_isvoid(obj)) { + keyfile = NULL; /* disable it */ + } else { + keyfile = cfg_obj_asstring(obj); + } + } else { + keyfile = named_g_defaultsessionkeyfile; + } + + obj = NULL; + result = named_config_get(maps, "session-keyname", &obj); + INSIST(result == ISC_R_SUCCESS); + keynamestr = cfg_obj_asstring(obj); + isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr)); + isc_buffer_add(&buffer, strlen(keynamestr)); + keyname = dns_fixedname_initname(&fname); + result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = NULL; + result = named_config_get(maps, "session-keyalg", &obj); + INSIST(result == ISC_R_SUCCESS); + algstr = cfg_obj_asstring(obj); + algname = NULL; + result = named_config_getkeyalgorithm2(algstr, &algname, &algtype, + &bits); + if (result != ISC_R_SUCCESS) { + const char *s = " (keeping current key)"; + + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "session-keyalg: " + "unsupported or unknown algorithm '%s'%s", + algstr, server->session_keyfile != NULL ? s : ""); + return (result); + } + + /* See if we need to (re)generate a new key. */ + if (keyfile == NULL) { + if (server->session_keyfile != NULL) { + need_deleteold = true; + } + } else if (server->session_keyfile == NULL) { + need_createnew = true; + } else if (strcmp(keyfile, server->session_keyfile) != 0 || + !dns_name_equal(server->session_keyname, keyname) || + server->session_keyalg != algtype || + server->session_keybits != bits) + { + need_deleteold = true; + need_createnew = true; + } + + if (need_deleteold) { + INSIST(server->session_keyfile != NULL); + INSIST(server->session_keyname != NULL); + INSIST(server->sessionkey != NULL); + + cleanup_session_key(server, mctx); + } + + if (need_createnew) { + INSIST(server->sessionkey == NULL); + INSIST(server->session_keyfile == NULL); + INSIST(server->session_keyname == NULL); + INSIST(server->session_keyalg == DST_ALG_UNKNOWN); + INSIST(server->session_keybits == 0); + + server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t)); + dns_name_init(server->session_keyname, NULL); + dns_name_dup(keyname, mctx, server->session_keyname); + + server->session_keyfile = isc_mem_strdup(mctx, keyfile); + + server->session_keyalg = algtype; + server->session_keybits = bits; + + CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr, + algtype, bits, mctx, first_time, + &server->sessionkey)); + } + + return (result); + +cleanup: + cleanup_session_key(server, mctx); + return (result); +} + +#ifndef HAVE_LMDB +static isc_result_t +count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) { + isc_result_t result; + + /* The new zone file may not exist. That is OK. */ + if (!isc_file_exists(view->new_zone_file)) { + *num_zonesp = 0; + return (ISC_R_SUCCESS); + } + + /* + * In the case of NZF files, we also parse the configuration in + * the file at this stage. + * + * This may be called in multiple views, so we reset + * the parser each time. + */ + cfg_parser_reset(named_g_addparser); + result = cfg_parse_file(named_g_addparser, view->new_zone_file, + &cfg_type_addzoneconf, &nzcfg->nzf_config); + if (result == ISC_R_SUCCESS) { + int num_zones; + + num_zones = count_zones(nzcfg->nzf_config); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "NZF file '%s' contains %d zones", + view->new_zone_file, num_zones); + if (num_zonesp != NULL) { + *num_zonesp = num_zones; + } + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error parsing NZF file '%s': %s", + view->new_zone_file, isc_result_totext(result)); + } + + return (result); +} + +#else /* HAVE_LMDB */ + +static isc_result_t +count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) { + isc_result_t result; + int n; + + UNUSED(nzcfg); + + REQUIRE(num_zonesp != NULL); + + LOCK(&view->new_zone_lock); + + CHECK(migrate_nzf(view)); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "loading NZD zone count from '%s' " + "for view '%s'", + view->new_zone_db, view->name); + + CHECK(nzd_count(view, &n)); + + *num_zonesp = n; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "NZD database '%s' contains %d zones", view->new_zone_db, + n); + +cleanup: + if (result != ISC_R_SUCCESS) { + *num_zonesp = 0; + } + + UNLOCK(&view->new_zone_lock); + + return (ISC_R_SUCCESS); +} + +#endif /* HAVE_LMDB */ + +static isc_result_t +setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, + cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx, + int *num_zones) { + isc_result_t result = ISC_R_SUCCESS; + bool allow = false; + ns_cfgctx_t *nzcfg = NULL; + const cfg_obj_t *maps[4]; + const cfg_obj_t *options = NULL, *voptions = NULL; + const cfg_obj_t *nz = NULL; + const cfg_obj_t *nzdir = NULL; + const char *dir = NULL; + const cfg_obj_t *obj = NULL; + int i = 0; + uint64_t mapsize = 0ULL; + + REQUIRE(config != NULL); + + if (vconfig != NULL) { + voptions = cfg_tuple_get(vconfig, "options"); + } + if (voptions != NULL) { + maps[i++] = voptions; + } + result = cfg_map_get(config, "options", &options); + if (result == ISC_R_SUCCESS) { + maps[i++] = options; + } + maps[i++] = named_g_defaults; + maps[i] = NULL; + + result = named_config_get(maps, "allow-new-zones", &nz); + if (result == ISC_R_SUCCESS) { + allow = cfg_obj_asboolean(nz); + } + result = named_config_get(maps, "new-zones-directory", &nzdir); + if (result == ISC_R_SUCCESS) { + dir = cfg_obj_asstring(nzdir); + result = isc_file_isdirectory(dir); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "invalid new-zones-directory %s: %s", dir, + isc_result_totext(result)); + return (result); + } + if (!isc_file_isdirwritable(dir)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "new-zones-directory '%s' " + "is not writable", + dir); + return (ISC_R_NOPERM); + } + + dns_view_setnewzonedir(view, dir); + } + +#ifdef HAVE_LMDB + result = named_config_get(maps, "lmdb-mapsize", &obj); + if (result == ISC_R_SUCCESS && obj != NULL) { + mapsize = cfg_obj_asuint64(obj); + if (mapsize < (1ULL << 20)) { /* 1 megabyte */ + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "'lmdb-mapsize " + "%" PRId64 "' " + "is too small", + mapsize); + return (ISC_R_FAILURE); + } else if (mapsize > (1ULL << 40)) { /* 1 terabyte */ + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "'lmdb-mapsize " + "%" PRId64 "' " + "is too large", + mapsize); + return (ISC_R_FAILURE); + } + } +#else /* ifdef HAVE_LMDB */ + UNUSED(obj); +#endif /* HAVE_LMDB */ + + /* + * A non-empty catalog-zones statement implies allow-new-zones + */ + if (!allow) { + const cfg_obj_t *cz = NULL; + result = named_config_get(maps, "catalog-zones", &cz); + if (result == ISC_R_SUCCESS) { + const cfg_listelt_t *e = + cfg_list_first(cfg_tuple_get(cz, "zone list")); + if (e != NULL) { + allow = true; + } + } + } + + if (!allow) { + dns_view_setnewzones(view, false, NULL, NULL, 0ULL); + if (num_zones != NULL) { + *num_zones = 0; + } + return (ISC_R_SUCCESS); + } + + nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg)); + + /* + * We attach the parser that was used for config as well + * as the one that will be used for added zones, to avoid + * a shutdown race later. + */ + memset(nzcfg, 0, sizeof(*nzcfg)); + cfg_parser_attach(conf_parser, &nzcfg->conf_parser); + cfg_parser_attach(named_g_addparser, &nzcfg->add_parser); + isc_mem_attach(view->mctx, &nzcfg->mctx); + cfg_aclconfctx_attach(actx, &nzcfg->actx); + + result = dns_view_setnewzones(view, true, nzcfg, newzone_cfgctx_destroy, + mapsize); + if (result != ISC_R_SUCCESS) { + dns_view_setnewzones(view, false, NULL, NULL, 0ULL); + return (result); + } + + cfg_obj_attach(config, &nzcfg->config); + if (vconfig != NULL) { + cfg_obj_attach(vconfig, &nzcfg->vconfig); + } + + result = count_newzones(view, nzcfg, num_zones); + return (result); +} + +static void +configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig, + dns_view_t *view) { + const char *zname; + dns_fixedname_t fixorigin; + dns_name_t *origin; + isc_result_t result2; + dns_view_t *pview = NULL; + dns_zone_t *zone = NULL; + + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + origin = dns_fixedname_initname(&fixorigin); + + result2 = dns_name_fromstring(origin, zname, 0, NULL); + if (result2 != ISC_R_SUCCESS) { + return; + } + + result2 = dns_viewlist_find(&named_g_server->viewlist, view->name, + view->rdclass, &pview); + if (result2 != ISC_R_SUCCESS) { + return; + } + + result2 = dns_view_findzone(pview, origin, &zone); + if (result2 != ISC_R_SUCCESS) { + dns_view_detach(&pview); + return; + } + + if (result == ISC_R_SUCCESS) { + dns_zone_setviewcommit(zone); + } else { + dns_zone_setviewrevert(zone); + } + + dns_zone_detach(&zone); + dns_view_detach(&pview); +} + +#ifndef HAVE_LMDB + +static isc_result_t +configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, + isc_mem_t *mctx, cfg_aclconfctx_t *actx) { + isc_result_t result; + ns_cfgctx_t *nzctx; + const cfg_obj_t *zonelist; + const cfg_listelt_t *element; + + nzctx = view->new_zone_config; + if (nzctx == NULL || nzctx->nzf_config == NULL) { + return (ISC_R_SUCCESS); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "loading additional zones for view '%s'", view->name); + + zonelist = NULL; + cfg_map_get(nzctx->nzf_config, "zone", &zonelist); + + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig = cfg_listelt_value(element); + CHECK(configure_zone(config, zconfig, vconfig, mctx, view, + &named_g_server->viewlist, + &named_g_server->kasplist, actx, true, + false, false)); + } + + result = ISC_R_SUCCESS; + +cleanup: + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig = cfg_listelt_value(element); + configure_zone_setviewcommit(result, zconfig, view); + } + + return (result); +} + +#else /* HAVE_LMDB */ + +static isc_result_t +data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data, isc_buffer_t **text, + cfg_obj_t **zoneconfig) { + isc_result_t result; + const char *zone_name; + size_t zone_name_len; + const char *zone_config; + size_t zone_config_len; + cfg_obj_t *zoneconf = NULL; + char bufname[DNS_NAME_FORMATSIZE]; + + REQUIRE(view != NULL); + REQUIRE(key != NULL); + REQUIRE(data != NULL); + REQUIRE(text != NULL); + REQUIRE(zoneconfig != NULL && *zoneconfig == NULL); + + if (*text == NULL) { + isc_buffer_allocate(view->mctx, text, 256); + } else { + isc_buffer_clear(*text); + } + + zone_name = (const char *)key->mv_data; + zone_name_len = key->mv_size; + INSIST(zone_name != NULL && zone_name_len > 0); + + zone_config = (const char *)data->mv_data; + zone_config_len = data->mv_size; + INSIST(zone_config != NULL && zone_config_len > 0); + + /* zone zonename { config; }; */ + result = isc_buffer_reserve(text, 6 + zone_name_len + 2 + + zone_config_len + 2); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + CHECK(putstr(text, "zone \"")); + CHECK(putmem(text, (const void *)zone_name, zone_name_len)); + CHECK(putstr(text, "\" ")); + CHECK(putmem(text, (const void *)zone_config, zone_config_len)); + CHECK(putstr(text, ";\n")); + + snprintf(bufname, sizeof(bufname), "%.*s", (int)zone_name_len, + zone_name); + + cfg_parser_reset(named_g_addparser); + result = cfg_parse_buffer(named_g_addparser, *text, bufname, 0, + &cfg_type_addzoneconf, 0, &zoneconf); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "parsing config for zone '%.*s' in " + "NZD database '%s' failed", + (int)zone_name_len, zone_name, view->new_zone_db); + goto cleanup; + } + + *zoneconfig = zoneconf; + zoneconf = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (zoneconf != NULL) { + cfg_obj_destroy(named_g_addparser, &zoneconf); + } + + return (result); +} + +/*% + * Prototype for a callback which can be used with for_all_newzone_cfgs(). + */ +typedef isc_result_t (*newzone_cfg_cb_t)(const cfg_obj_t *zconfig, + cfg_obj_t *config, cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_view_t *view, + cfg_aclconfctx_t *actx); + +/*% + * For each zone found in a NZD opened by the caller, create an object + * representing its configuration and invoke "callback" with the created + * object, "config", "vconfig", "mctx", "view" and "actx" as arguments (all + * these are non-global variables required to invoke configure_zone()). + * Immediately interrupt processing if an error is encountered while + * transforming NZD data into a zone configuration object or if "callback" + * returns an error. + * + * Caller must hold 'view->new_zone_lock'. + */ +static isc_result_t +for_all_newzone_cfgs(newzone_cfg_cb_t callback, cfg_obj_t *config, + cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, + cfg_aclconfctx_t *actx, MDB_txn *txn, MDB_dbi dbi) { + const cfg_obj_t *zconfig, *zlist; + isc_result_t result = ISC_R_SUCCESS; + cfg_obj_t *zconfigobj = NULL; + isc_buffer_t *text = NULL; + MDB_cursor *cursor = NULL; + MDB_val data, key; + int status; + + status = mdb_cursor_open(txn, dbi, &cursor); + if (status != MDB_SUCCESS) { + return (ISC_R_FAILURE); + } + + for (status = mdb_cursor_get(cursor, &key, &data, MDB_FIRST); + status == MDB_SUCCESS; + status = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) + { + /* + * Create a configuration object from data fetched from NZD. + */ + result = data_to_cfg(view, &key, &data, &text, &zconfigobj); + if (result != ISC_R_SUCCESS) { + break; + } + + /* + * Extract zone configuration from configuration object. + */ + zlist = NULL; + result = cfg_map_get(zconfigobj, "zone", &zlist); + if (result != ISC_R_SUCCESS) { + break; + } else if (!cfg_obj_islist(zlist)) { + result = ISC_R_FAILURE; + break; + } + zconfig = cfg_listelt_value(cfg_list_first(zlist)); + + /* + * Invoke callback. + */ + result = callback(zconfig, config, vconfig, mctx, view, actx); + if (result != ISC_R_SUCCESS) { + break; + } + + /* + * Destroy the configuration object created in this iteration. + */ + cfg_obj_destroy(named_g_addparser, &zconfigobj); + } + + if (text != NULL) { + isc_buffer_free(&text); + } + if (zconfigobj != NULL) { + cfg_obj_destroy(named_g_addparser, &zconfigobj); + } + mdb_cursor_close(cursor); + + return (result); +} + +/*% + * Attempt to configure a zone found in NZD and return the result. + */ +static isc_result_t +configure_newzone(const cfg_obj_t *zconfig, cfg_obj_t *config, + cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, + cfg_aclconfctx_t *actx) { + return (configure_zone( + config, zconfig, vconfig, mctx, view, &named_g_server->viewlist, + &named_g_server->kasplist, actx, true, false, false)); +} + +/*% + * Revert new view assignment for a zone found in NZD. + */ +static isc_result_t +configure_newzone_revert(const cfg_obj_t *zconfig, cfg_obj_t *config, + cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view, + cfg_aclconfctx_t *actx) { + UNUSED(config); + UNUSED(vconfig); + UNUSED(mctx); + UNUSED(actx); + + configure_zone_setviewcommit(ISC_R_FAILURE, zconfig, view); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, + isc_mem_t *mctx, cfg_aclconfctx_t *actx) { + isc_result_t result; + MDB_txn *txn = NULL; + MDB_dbi dbi; + + if (view->new_zone_config == NULL) { + return (ISC_R_SUCCESS); + } + + LOCK(&view->new_zone_lock); + + result = nzd_open(view, MDB_RDONLY, &txn, &dbi); + if (result != ISC_R_SUCCESS) { + UNLOCK(&view->new_zone_lock); + return (ISC_R_SUCCESS); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "loading NZD configs from '%s' " + "for view '%s'", + view->new_zone_db, view->name); + + result = for_all_newzone_cfgs(configure_newzone, config, vconfig, mctx, + view, actx, txn, dbi); + if (result != ISC_R_SUCCESS) { + /* + * An error was encountered while attempting to configure zones + * found in NZD. As this error may have been caused by a + * configure_zone() failure, try restoring a sane configuration + * by reattaching all zones found in NZD to the old view. If + * this also fails, too bad, there is nothing more we can do in + * terms of trying to make things right. + */ + (void)for_all_newzone_cfgs(configure_newzone_revert, config, + vconfig, mctx, view, actx, txn, dbi); + } + + (void)nzd_close(&txn, false); + + UNLOCK(&view->new_zone_lock); + + return (result); +} + +static isc_result_t +get_newzone_config(dns_view_t *view, const char *zonename, + cfg_obj_t **zoneconfig) { + isc_result_t result; + int status; + cfg_obj_t *zoneconf = NULL; + isc_buffer_t *text = NULL; + MDB_txn *txn = NULL; + MDB_dbi dbi; + MDB_val key, data; + char zname[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fname; + dns_name_t *name; + isc_buffer_t b; + + INSIST(zoneconfig != NULL && *zoneconfig == NULL); + + LOCK(&view->new_zone_lock); + + CHECK(nzd_open(view, MDB_RDONLY, &txn, &dbi)); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "loading NZD config from '%s' " + "for zone '%s'", + view->new_zone_db, zonename); + + /* Normalize zone name */ + isc_buffer_constinit(&b, zonename, strlen(zonename)); + isc_buffer_add(&b, strlen(zonename)); + name = dns_fixedname_initname(&fname); + CHECK(dns_name_fromtext(name, &b, dns_rootname, DNS_NAME_DOWNCASE, + NULL)); + dns_name_format(name, zname, sizeof(zname)); + + key.mv_data = zname; + key.mv_size = strlen(zname); + + status = mdb_get(txn, dbi, &key, &data); + if (status != MDB_SUCCESS) { + CHECK(ISC_R_FAILURE); + } + + CHECK(data_to_cfg(view, &key, &data, &text, &zoneconf)); + + *zoneconfig = zoneconf; + zoneconf = NULL; + result = ISC_R_SUCCESS; + +cleanup: + (void)nzd_close(&txn, false); + + UNLOCK(&view->new_zone_lock); + + if (zoneconf != NULL) { + cfg_obj_destroy(named_g_addparser, &zoneconf); + } + if (text != NULL) { + isc_buffer_free(&text); + } + + return (result); +} + +#endif /* HAVE_LMDB */ + +static int +count_zones(const cfg_obj_t *conf) { + const cfg_obj_t *zonelist = NULL; + const cfg_listelt_t *element; + int n = 0; + + REQUIRE(conf != NULL); + + cfg_map_get(conf, "zone", &zonelist); + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + n++; + } + + return (n); +} + +static isc_result_t +check_lockfile(named_server_t *server, const cfg_obj_t *config, + bool first_time) { + isc_result_t result; + const char *filename = NULL; + const cfg_obj_t *maps[3]; + const cfg_obj_t *options; + const cfg_obj_t *obj; + int i; + + i = 0; + options = NULL; + result = cfg_map_get(config, "options", &options); + if (result == ISC_R_SUCCESS) { + maps[i++] = options; + } + maps[i++] = named_g_defaults; + maps[i] = NULL; + + obj = NULL; + (void)named_config_get(maps, "lock-file", &obj); + + if (!first_time) { + if (obj != NULL && !cfg_obj_isstring(obj) && + server->lockfile != NULL && + strcmp(cfg_obj_asstring(obj), server->lockfile) != 0) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "changing 'lock-file' " + "has no effect until the " + "server is restarted"); + } + + return (ISC_R_SUCCESS); + } + + if (obj != NULL) { + if (cfg_obj_isvoid(obj)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "skipping lock-file check "); + return (ISC_R_SUCCESS); + } else if (named_g_forcelock) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "'lock-file' has no effect " + "because the server was run with -X"); + server->lockfile = isc_mem_strdup( + server->mctx, named_g_defaultlockfile); + } else { + filename = cfg_obj_asstring(obj); + server->lockfile = isc_mem_strdup(server->mctx, + filename); + } + + if (server->lockfile == NULL) { + return (ISC_R_NOMEMORY); + } + } + + if (named_g_forcelock && named_g_defaultlockfile != NULL) { + INSIST(server->lockfile == NULL); + server->lockfile = isc_mem_strdup(server->mctx, + named_g_defaultlockfile); + } + + if (server->lockfile == NULL) { + return (ISC_R_SUCCESS); + } + + if (named_os_issingleton(server->lockfile)) { + return (ISC_R_SUCCESS); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "could not lock %s; another named " + "process may be running", + server->lockfile); + return (ISC_R_FAILURE); +} + +static isc_result_t +load_configuration(const char *filename, named_server_t *server, + bool first_time) { + cfg_obj_t *config = NULL, *bindkeys = NULL; + cfg_parser_t *conf_parser = NULL, *bindkeys_parser = NULL; + const cfg_listelt_t *element; + const cfg_obj_t *builtin_views; + const cfg_obj_t *maps[3]; + const cfg_obj_t *obj; + const cfg_obj_t *options; + const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports; + const cfg_obj_t *kasps; + dns_kasp_t *kasp = NULL; + dns_kasp_t *kasp_next = NULL; + dns_kasp_t *default_kasp = NULL; + dns_kasplist_t tmpkasplist, kasplist; + const cfg_obj_t *views; + dns_view_t *view = NULL; + dns_view_t *view_next = NULL; + dns_viewlist_t tmpviewlist; + dns_viewlist_t viewlist, builtin_viewlist; + in_port_t listen_port, udpport_low, udpport_high; + int i, backlog; + int num_zones = 0; + bool exclusive = false; + isc_interval_t interval; + isc_logconfig_t *logc = NULL; + isc_portset_t *v4portset = NULL; + isc_portset_t *v6portset = NULL; + isc_result_t result, tresult; + uint32_t heartbeat_interval; + uint32_t interface_interval; + uint32_t udpsize; + uint32_t transfer_message_size; + uint32_t recv_tcp_buffer_size; + uint32_t send_tcp_buffer_size; + uint32_t recv_udp_buffer_size; + uint32_t send_udp_buffer_size; + named_cache_t *nsc; + named_cachelist_t cachelist, tmpcachelist; + ns_altsecret_t *altsecret; + ns_altsecretlist_t altsecrets, tmpaltsecrets; + uint32_t softquota = 0; + uint32_t max; + uint64_t initial, idle, keepalive, advertised; + bool loadbalancesockets; + dns_aclenv_t *env = + ns_interfacemgr_getaclenv(named_g_server->interfacemgr); + + ISC_LIST_INIT(kasplist); + ISC_LIST_INIT(viewlist); + ISC_LIST_INIT(builtin_viewlist); + ISC_LIST_INIT(cachelist); + ISC_LIST_INIT(altsecrets); + + /* Create the ACL configuration context */ + if (named_g_aclconfctx != NULL) { + cfg_aclconfctx_detach(&named_g_aclconfctx); + } + CHECK(cfg_aclconfctx_create(named_g_mctx, &named_g_aclconfctx)); + + /* + * Shut down all dyndb instances. + */ + dns_dyndb_cleanup(false); + + /* + * Parse the global default pseudo-config file. + */ + if (first_time) { + result = named_config_parsedefaults(named_g_parser, + &named_g_config); + if (result != ISC_R_SUCCESS) { + named_main_earlyfatal("unable to load " + "internal defaults: %s", + isc_result_totext(result)); + } + RUNTIME_CHECK(cfg_map_get(named_g_config, "options", + &named_g_defaults) == ISC_R_SUCCESS); + } + + /* + * Parse the configuration file using the new config code. + */ + config = NULL; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "loading configuration from '%s'", filename); + CHECK(cfg_parser_create(named_g_mctx, named_g_lctx, &conf_parser)); + cfg_parser_setcallback(conf_parser, directory_callback, NULL); + result = cfg_parse_file(conf_parser, filename, &cfg_type_namedconf, + &config); + + CHECK(result); + + /* + * Check the validity of the configuration. + * + * (Ignore plugin parameters for now; they will be + * checked later when the modules are actually loaded and + * registered.) + */ + CHECK(bind9_check_namedconf(config, false, false, named_g_lctx, + named_g_mctx)); + + /* Let's recreate the TLS context cache */ + if (server->tlsctx_server_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_server_cache); + } + + isc_tlsctx_cache_create(named_g_mctx, &server->tlsctx_server_cache); + + if (server->tlsctx_client_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_client_cache); + } + + isc_tlsctx_cache_create(named_g_mctx, &server->tlsctx_client_cache); + + dns_zonemgr_set_tlsctx_cache(server->zonemgr, + server->tlsctx_client_cache); + + /* + * Fill in the maps array, used for resolving defaults. + */ + i = 0; + options = NULL; + result = cfg_map_get(config, "options", &options); + if (result == ISC_R_SUCCESS) { + maps[i++] = options; + } + maps[i++] = named_g_defaults; + maps[i] = NULL; + +#if HAVE_LIBNGHTTP2 + obj = NULL; + result = named_config_get(maps, "http-port", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_httpport = (in_port_t)cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "https-port", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_httpsport = (in_port_t)cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "http-listener-clients", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_http_listener_clients = cfg_obj_asuint32(obj); + + obj = NULL; + result = named_config_get(maps, "http-streams-per-connection", &obj); + INSIST(result == ISC_R_SUCCESS); + named_g_http_streams_per_conn = cfg_obj_asuint32(obj); +#endif + + /* + * If bind.keys exists, load it. If "dnssec-validation auto" + * is turned on, the root key found there will be used as a + * default trust anchor. + */ + obj = NULL; + result = named_config_get(maps, "bindkeys-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->bindkeysfile, cfg_obj_asstring(obj)), + "strdup"); + INSIST(server->bindkeysfile != NULL); + + if (access(server->bindkeysfile, R_OK) == 0) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "reading built-in trust anchors " + "from file '%s'", + server->bindkeysfile); + + CHECK(cfg_parser_create(named_g_mctx, named_g_lctx, + &bindkeys_parser)); + + result = cfg_parse_file(bindkeys_parser, server->bindkeysfile, + &cfg_type_bindkeys, &bindkeys); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "unable to parse '%s' error '%s'; using " + "built-in keys instead", + server->bindkeysfile, + isc_result_totext(result)); + } + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "unable to open '%s'; using built-in keys " + "instead", + server->bindkeysfile); + } + + /* Ensure exclusive access to configuration data. */ + if (!exclusive) { + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + exclusive = true; + } + + /* + * Set process limits, which (usually) needs to be done as root. + */ + set_limits(maps); + + /* + * Check the process lockfile. + */ + CHECK(check_lockfile(server, config, first_time)); + +#if defined(HAVE_GEOIP2) + /* + * Release any previously opened GeoIP2 databases. + */ + named_geoip_unload(); + + /* + * Initialize GeoIP databases from the configured location. + * This should happen before configuring any ACLs, so that we + * know what databases are available and can reject any GeoIP + * ACLs that can't work. + */ + obj = NULL; + result = named_config_get(maps, "geoip-directory", &obj); + INSIST(result == ISC_R_SUCCESS); + if (cfg_obj_isstring(obj)) { + char *dir; + DE_CONST(cfg_obj_asstring(obj), dir); + named_geoip_load(dir); + } + named_g_aclconfctx->geoip = named_g_geoip; +#endif /* HAVE_GEOIP2 */ + + /* + * Configure various server options. + */ + configure_server_quota(maps, "transfers-out", + &server->sctx->xfroutquota); + configure_server_quota(maps, "tcp-clients", &server->sctx->tcpquota); + configure_server_quota(maps, "recursive-clients", + &server->sctx->recursionquota); + configure_server_quota(maps, "update-quota", &server->sctx->updquota); + + max = isc_quota_getmax(&server->sctx->recursionquota); + if (max > 1000) { + unsigned margin = ISC_MAX(100, named_g_cpus + 1); + if (margin + 100 > max) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "'recursive-clients %d' too low when " + "running with %d worker threads", + max, named_g_cpus); + CHECK(ISC_R_RANGE); + } + softquota = max - margin; + } else { + softquota = (max * 90) / 100; + } + + isc_quota_soft(&server->sctx->recursionquota, softquota); + + /* + * Set "blackhole". Only legal at options level; there is + * no default. + */ + CHECK(configure_view_acl(NULL, config, NULL, "blackhole", NULL, + named_g_aclconfctx, named_g_mctx, + &server->sctx->blackholeacl)); + if (server->sctx->blackholeacl != NULL) { + dns_dispatchmgr_setblackhole(named_g_dispatchmgr, + server->sctx->blackholeacl); + } + + /* + * Set "keep-response-order". Only legal at options or + * global defaults level. + */ + CHECK(configure_view_acl(NULL, config, named_g_config, + "keep-response-order", NULL, + named_g_aclconfctx, named_g_mctx, + &server->sctx->keepresporder)); + + obj = NULL; + result = named_config_get(maps, "match-mapped-addresses", &obj); + INSIST(result == ISC_R_SUCCESS); + env->match_mapped = cfg_obj_asboolean(obj); + + CHECKM(named_statschannels_configure(named_g_server, config, + named_g_aclconfctx), + "configuring statistics server(s)"); + + /* + * Configure the network manager + */ + obj = NULL; + result = named_config_get(maps, "tcp-initial-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + initial = cfg_obj_asuint32(obj) * 100; + if (initial > MAX_INITIAL_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-initial-timeout value is out of range: " + "lowering to %" PRIu32, + MAX_INITIAL_TIMEOUT / 100); + initial = MAX_INITIAL_TIMEOUT; + } else if (initial < MIN_INITIAL_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-initial-timeout value is out of range: " + "raising to %" PRIu32, + MIN_INITIAL_TIMEOUT / 100); + initial = MIN_INITIAL_TIMEOUT; + } + + obj = NULL; + result = named_config_get(maps, "tcp-idle-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + idle = cfg_obj_asuint32(obj) * 100; + if (idle > MAX_IDLE_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-idle-timeout value is out of range: " + "lowering to %" PRIu32, + MAX_IDLE_TIMEOUT / 100); + idle = MAX_IDLE_TIMEOUT; + } else if (idle < MIN_IDLE_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-idle-timeout value is out of range: " + "raising to %" PRIu32, + MIN_IDLE_TIMEOUT / 100); + idle = MIN_IDLE_TIMEOUT; + } + + obj = NULL; + result = named_config_get(maps, "tcp-keepalive-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + keepalive = cfg_obj_asuint32(obj) * 100; + if (keepalive > MAX_KEEPALIVE_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-keepalive-timeout value is out of range: " + "lowering to %" PRIu32, + MAX_KEEPALIVE_TIMEOUT / 100); + keepalive = MAX_KEEPALIVE_TIMEOUT; + } else if (keepalive < MIN_KEEPALIVE_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-keepalive-timeout value is out of range: " + "raising to %" PRIu32, + MIN_KEEPALIVE_TIMEOUT / 100); + keepalive = MIN_KEEPALIVE_TIMEOUT; + } + + obj = NULL; + result = named_config_get(maps, "tcp-advertised-timeout", &obj); + INSIST(result == ISC_R_SUCCESS); + advertised = cfg_obj_asuint32(obj) * 100; + if (advertised > MAX_ADVERTISED_TIMEOUT) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "tcp-advertized-timeout value is out of range: " + "lowering to %" PRIu32, + MAX_ADVERTISED_TIMEOUT / 100); + advertised = MAX_ADVERTISED_TIMEOUT; + } + + isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive, + advertised); + +#define CAP_IF_NOT_ZERO(v, min, max) \ + if (v > 0 && v < min) { \ + v = min; \ + } else if (v > max) { \ + v = max; \ + } + + /* Set the kernel send and receive buffer sizes */ + obj = NULL; + result = named_config_get(maps, "tcp-receive-buffer", &obj); + INSIST(result == ISC_R_SUCCESS); + recv_tcp_buffer_size = cfg_obj_asuint32(obj); + CAP_IF_NOT_ZERO(recv_tcp_buffer_size, 4096, INT32_MAX); + + obj = NULL; + result = named_config_get(maps, "tcp-send-buffer", &obj); + INSIST(result == ISC_R_SUCCESS); + send_tcp_buffer_size = cfg_obj_asuint32(obj); + CAP_IF_NOT_ZERO(send_tcp_buffer_size, 4096, INT32_MAX); + + obj = NULL; + result = named_config_get(maps, "udp-receive-buffer", &obj); + INSIST(result == ISC_R_SUCCESS); + recv_udp_buffer_size = cfg_obj_asuint32(obj); + CAP_IF_NOT_ZERO(recv_udp_buffer_size, 4096, INT32_MAX); + + obj = NULL; + result = named_config_get(maps, "udp-send-buffer", &obj); + INSIST(result == ISC_R_SUCCESS); + send_udp_buffer_size = cfg_obj_asuint32(obj); + CAP_IF_NOT_ZERO(send_udp_buffer_size, 4096, INT32_MAX); + + isc_nm_setnetbuffers(named_g_netmgr, recv_tcp_buffer_size, + send_tcp_buffer_size, recv_udp_buffer_size, + send_udp_buffer_size); + +#undef CAP_IF_NOT_ZERO + + /* + * Configure sets of UDP query source ports. + */ + CHECKM(isc_portset_create(named_g_mctx, &v4portset), "creating UDP " + "port set"); + CHECKM(isc_portset_create(named_g_mctx, &v6portset), "creating UDP " + "port set"); + + usev4ports = NULL; + usev6ports = NULL; + avoidv4ports = NULL; + avoidv6ports = NULL; + + (void)named_config_get(maps, "use-v4-udp-ports", &usev4ports); + if (usev4ports != NULL) { + portset_fromconf(v4portset, usev4ports, true); + } else { + CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low, + &udpport_high), + "get the default UDP/IPv4 port range"); + if (udpport_low == udpport_high) { + isc_portset_add(v4portset, udpport_low); + } else { + isc_portset_addrange(v4portset, udpport_low, + udpport_high); + } + if (!ns_server_getoption(server->sctx, NS_SERVER_DISABLE4)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "using default UDP/IPv4 port range: " + "[%d, %d]", + udpport_low, udpport_high); + } + } + (void)named_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports); + if (avoidv4ports != NULL) { + portset_fromconf(v4portset, avoidv4ports, false); + } + + (void)named_config_get(maps, "use-v6-udp-ports", &usev6ports); + if (usev6ports != NULL) { + portset_fromconf(v6portset, usev6ports, true); + } else { + CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low, + &udpport_high), + "get the default UDP/IPv6 port range"); + if (udpport_low == udpport_high) { + isc_portset_add(v6portset, udpport_low); + } else { + isc_portset_addrange(v6portset, udpport_low, + udpport_high); + } + if (!ns_server_getoption(server->sctx, NS_SERVER_DISABLE6)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "using default UDP/IPv6 port range: " + "[%d, %d]", + udpport_low, udpport_high); + } + } + (void)named_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports); + if (avoidv6ports != NULL) { + portset_fromconf(v6portset, avoidv6ports, false); + } + + dns_dispatchmgr_setavailports(named_g_dispatchmgr, v4portset, + v6portset); + + /* + * Set the EDNS UDP size when we don't match a view. + */ + obj = NULL; + result = named_config_get(maps, "edns-udp-size", &obj); + INSIST(result == ISC_R_SUCCESS); + udpsize = cfg_obj_asuint32(obj); + if (udpsize < 512) { + udpsize = 512; + } + if (udpsize > 4096) { + udpsize = 4096; + } + server->sctx->udpsize = (uint16_t)udpsize; + + /* Set the transfer message size for TCP */ + obj = NULL; + result = named_config_get(maps, "transfer-message-size", &obj); + INSIST(result == ISC_R_SUCCESS); + transfer_message_size = cfg_obj_asuint32(obj); + if (transfer_message_size < 512) { + transfer_message_size = 512; + } else if (transfer_message_size > 65535) { + transfer_message_size = 65535; + } + server->sctx->transfer_tcp_message_size = + (uint16_t)transfer_message_size; + + /* + * Configure the zone manager. + */ + obj = NULL; + result = named_config_get(maps, "transfers-in", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "transfers-per-ns", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "notify-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setnotifyrate(server->zonemgr, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "startup-notify-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setstartupnotifyrate(server->zonemgr, + cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "serial-query-rate", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj)); + + /* + * Determine which port to use for listening for incoming connections. + */ + if (named_g_port != 0) { + listen_port = named_g_port; + } else { + CHECKM(named_config_getport(config, "port", &listen_port), + "port"); + } + + /* + * Find the listen queue depth. + */ + obj = NULL; + result = named_config_get(maps, "tcp-listen-queue", &obj); + INSIST(result == ISC_R_SUCCESS); + backlog = cfg_obj_asuint32(obj); + if ((backlog > 0) && (backlog < 10)) { + backlog = 10; + } + ns_interfacemgr_setbacklog(server->interfacemgr, backlog); + + obj = NULL; + result = named_config_get(maps, "reuseport", &obj); + INSIST(result == ISC_R_SUCCESS); + loadbalancesockets = cfg_obj_asboolean(obj); +#if HAVE_SO_REUSEPORT_LB + if (first_time) { + isc_nm_setloadbalancesockets(named_g_netmgr, + cfg_obj_asboolean(obj)); + } else if (loadbalancesockets != + isc_nm_getloadbalancesockets(named_g_netmgr)) + { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "changing reuseport value requires server restart"); + } +#else + if (loadbalancesockets) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_WARNING, + "reuseport has no effect on this system"); + } +#endif + + /* + * Configure the interface manager according to the "listen-on" + * statement. + */ + { + const cfg_obj_t *clistenon = NULL; + ns_listenlist_t *listenon = NULL; + + clistenon = NULL; + /* + * Even though listen-on is present in the default + * configuration, this way is easier. + */ + if (options != NULL) { + (void)cfg_map_get(options, "listen-on", &clistenon); + } + if (clistenon != NULL) { + CHECK(listenlist_fromconfig( + clistenon, config, named_g_aclconfctx, + named_g_mctx, AF_INET, + server->tlsctx_server_cache, &listenon)); + } else { + /* + * Not specified, use default. + */ + CHECK(ns_listenlist_default(named_g_mctx, listen_port, + true, AF_INET, &listenon)); + } + if (listenon != NULL) { + ns_interfacemgr_setlistenon4(server->interfacemgr, + listenon); + ns_listenlist_detach(&listenon); + } + } + /* + * Ditto for IPv6. + */ + { + const cfg_obj_t *clistenon = NULL; + ns_listenlist_t *listenon = NULL; + + if (options != NULL) { + (void)cfg_map_get(options, "listen-on-v6", &clistenon); + } + if (clistenon != NULL) { + CHECK(listenlist_fromconfig( + clistenon, config, named_g_aclconfctx, + named_g_mctx, AF_INET6, + server->tlsctx_server_cache, &listenon)); + } else { + /* + * Not specified, use default. + */ + CHECK(ns_listenlist_default(named_g_mctx, listen_port, + true, AF_INET6, &listenon)); + } + if (listenon != NULL) { + ns_interfacemgr_setlistenon6(server->interfacemgr, + listenon); + ns_listenlist_detach(&listenon); + } + } + + /* + * Rescan the interface list to pick up changes in the + * listen-on option. It's important that we do this before we try + * to configure the query source, since the dispatcher we use might + * be shared with an interface. + */ + result = ns_interfacemgr_scan(server->interfacemgr, true, true); + + /* + * Check that named is able to TCP listen on at least one + * interface. Otherwise, another named process could be running + * and we should fail. + */ + if (first_time && (result == ISC_R_ADDRINUSE)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "unable to listen on any configured interfaces"); + result = ISC_R_FAILURE; + goto cleanup; + } + + /* + * Arrange for further interface scanning to occur periodically + * as specified by the "interface-interval" option. + */ + obj = NULL; + result = named_config_get(maps, "interface-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + interface_interval = cfg_obj_asduration(obj); + if (interface_interval == 0) { + CHECK(isc_timer_reset(server->interface_timer, + isc_timertype_inactive, NULL, NULL, + true)); + } else if (server->interface_interval != interface_interval) { + isc_interval_set(&interval, interface_interval, 0); + CHECK(isc_timer_reset(server->interface_timer, + isc_timertype_ticker, NULL, &interval, + false)); + } + server->interface_interval = interface_interval; + + /* + * Enable automatic interface scans. + */ + obj = NULL; + result = named_config_get(maps, "automatic-interface-scan", &obj); + INSIST(result == ISC_R_SUCCESS); + server->sctx->interface_auto = cfg_obj_asboolean(obj); + + /* + * Configure the dialup heartbeat timer. + */ + obj = NULL; + result = named_config_get(maps, "heartbeat-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + heartbeat_interval = cfg_obj_asuint32(obj) * 60; + if (heartbeat_interval == 0) { + CHECK(isc_timer_reset(server->heartbeat_timer, + isc_timertype_inactive, NULL, NULL, + true)); + } else if (server->heartbeat_interval != heartbeat_interval) { + isc_interval_set(&interval, heartbeat_interval, 0); + CHECK(isc_timer_reset(server->heartbeat_timer, + isc_timertype_ticker, NULL, &interval, + false)); + } + server->heartbeat_interval = heartbeat_interval; + + isc_interval_set(&interval, 1200, 0); + CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL, + &interval, false)); + + isc_interval_set(&interval, named_g_tat_interval, 0); + CHECK(isc_timer_reset(server->tat_timer, isc_timertype_ticker, NULL, + &interval, false)); + + /* + * Write the PID file. + */ + obj = NULL; + if (named_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS) { + if (cfg_obj_isvoid(obj)) { + named_os_writepidfile(NULL, first_time); + } else { + named_os_writepidfile(cfg_obj_asstring(obj), + first_time); + } + } else { + named_os_writepidfile(named_g_defaultpidfile, first_time); + } + + /* + * Configure the server-wide session key. This must be done before + * configure views because zone configuration may need to know + * session-keyname. + * + * Failure of session key generation isn't fatal at this time; if it + * turns out that a session key is really needed but doesn't exist, + * we'll treat it as a fatal error then. + */ + (void)configure_session_key(maps, server, named_g_mctx, first_time); + + /* + * Create the built-in kasp policies ("default", "insecure"). + */ + kasps = NULL; + (void)cfg_map_get(named_g_config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + + kasp = NULL; + CHECK(cfg_kasp_fromconfig(kconfig, default_kasp, named_g_mctx, + named_g_lctx, &kasplist, &kasp)); + INSIST(kasp != NULL); + dns_kasp_freeze(kasp); + + /* Insist that the first built-in policy is the default one. */ + if (default_kasp == NULL) { + INSIST(strcmp(dns_kasp_getname(kasp), "default") == 0); + dns_kasp_attach(kasp, &default_kasp); + } + + dns_kasp_detach(&kasp); + } + INSIST(default_kasp != NULL); + + /* + * Create the DNSSEC key and signing policies (KASP). + */ + kasps = NULL; + (void)cfg_map_get(config, "dnssec-policy", &kasps); + for (element = cfg_list_first(kasps); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *kconfig = cfg_listelt_value(element); + kasp = NULL; + CHECK(cfg_kasp_fromconfig(kconfig, default_kasp, named_g_mctx, + named_g_lctx, &kasplist, &kasp)); + INSIST(kasp != NULL); + dns_kasp_freeze(kasp); + dns_kasp_detach(&kasp); + } + + dns_kasp_detach(&default_kasp); + tmpkasplist = server->kasplist; + server->kasplist = kasplist; + kasplist = tmpkasplist; + + /* + * Configure the views. + */ + views = NULL; + (void)cfg_map_get(config, "view", &views); + + /* + * Create the views and count all the configured zones in + * order to correctly size the zone manager's task table. + * (We only count zones for configured views; the built-in + * "bind" view can be ignored as it only adds a negligible + * number of zones.) + * + * If we're allowing new zones, we need to be able to find the + * new zone file and count those as well. So we setup the new + * zone configuration context, but otherwise view configuration + * waits until after the zone manager's task list has been sized. + */ + for (element = cfg_list_first(views); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *vconfig = cfg_listelt_value(element); + const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options"); + int nzf_num_zones; + + view = NULL; + + CHECK(create_view(vconfig, &viewlist, &view)); + INSIST(view != NULL); + + num_zones += count_zones(voptions); + + CHECK(setup_newzones(view, config, vconfig, conf_parser, + named_g_aclconfctx, &nzf_num_zones)); + num_zones += nzf_num_zones; + + dns_view_detach(&view); + } + + /* + * If there were no explicit views then we do the default + * view here. + */ + if (views == NULL) { + int nzf_num_zones; + + CHECK(create_view(NULL, &viewlist, &view)); + INSIST(view != NULL); + + num_zones = count_zones(config); + + CHECK(setup_newzones(view, config, NULL, conf_parser, + named_g_aclconfctx, &nzf_num_zones)); + num_zones += nzf_num_zones; + + dns_view_detach(&view); + } + + /* + * Zones have been counted; set the zone manager task pool size. + */ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "sizing zone task pool based on %d zones", num_zones); + CHECK(dns_zonemgr_setsize(named_g_server->zonemgr, num_zones)); + + /* + * Configure and freeze all explicit views. Explicit + * views that have zones were already created at parsing + * time, but views with no zones must be created here. + */ + for (element = cfg_list_first(views); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *vconfig = cfg_listelt_value(element); + + view = NULL; + CHECK(find_view(vconfig, &viewlist, &view)); + CHECK(configure_view(view, &viewlist, config, vconfig, + &cachelist, &server->kasplist, bindkeys, + named_g_mctx, named_g_aclconfctx, true)); + dns_view_freeze(view); + dns_view_detach(&view); + } + + /* + * Make sure we have a default view if and only if there + * were no explicit views. + */ + if (views == NULL) { + view = NULL; + CHECK(find_view(NULL, &viewlist, &view)); + CHECK(configure_view(view, &viewlist, config, NULL, &cachelist, + &server->kasplist, bindkeys, named_g_mctx, + named_g_aclconfctx, true)); + dns_view_freeze(view); + dns_view_detach(&view); + } + + /* + * Create (or recreate) the built-in views. + */ + builtin_views = NULL; + RUNTIME_CHECK(cfg_map_get(named_g_config, "view", &builtin_views) == + ISC_R_SUCCESS); + for (element = cfg_list_first(builtin_views); element != NULL; + element = cfg_list_next(element)) + { + cfg_obj_t *vconfig = cfg_listelt_value(element); + + CHECK(create_view(vconfig, &builtin_viewlist, &view)); + CHECK(configure_view(view, &viewlist, config, vconfig, + &cachelist, &server->kasplist, bindkeys, + named_g_mctx, named_g_aclconfctx, false)); + dns_view_freeze(view); + dns_view_detach(&view); + view = NULL; + } + + /* Now combine the two viewlists into one */ + ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link); + + /* + * Commit any dns_zone_setview() calls on all zones in the new + * view. + */ + for (view = ISC_LIST_HEAD(viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + dns_view_setviewcommit(view); + } + + /* Swap our new view list with the production one. */ + tmpviewlist = server->viewlist; + server->viewlist = viewlist; + viewlist = tmpviewlist; + + /* Make the view list available to each of the views */ + view = ISC_LIST_HEAD(server->viewlist); + while (view != NULL) { + view->viewlist = &server->viewlist; + view = ISC_LIST_NEXT(view, link); + } + + /* Swap our new cache list with the production one. */ + tmpcachelist = server->cachelist; + server->cachelist = cachelist; + cachelist = tmpcachelist; + + /* Load the TKEY information from the configuration. */ + if (options != NULL) { + dns_tkeyctx_t *t = NULL; + CHECKM(named_tkeyctx_fromconfig(options, named_g_mctx, &t), + "configuring TKEY"); + if (server->sctx->tkeyctx != NULL) { + dns_tkeyctx_destroy(&server->sctx->tkeyctx); + } + server->sctx->tkeyctx = t; + } + + /* + * Bind the control port(s). + */ + CHECKM(named_controls_configure(named_g_server->controls, config, + named_g_aclconfctx), + "binding control channel(s)"); + +#ifdef HAVE_LMDB + /* + * If we're using LMDB, we may have created newzones databases + * as root, making it impossible to reopen them later after + * switching to a new userid. We close them now, and reopen + * after relinquishing privileges them. + */ + if (first_time) { + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + nzd_env_close(view); + } + } +#endif /* HAVE_LMDB */ + + /* + * Relinquish root privileges. + */ + if (first_time) { + named_os_changeuser(); + } + + /* + * Check that the working directory is writable. + */ + if (!isc_file_isdirwritable(".")) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "the working directory is not writable"); + result = ISC_R_NOPERM; + goto cleanup; + } + +#ifdef HAVE_LMDB + /* + * Reopen NZD databases. + */ + if (first_time) { + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + nzd_env_reopen(view); + } + } +#endif /* HAVE_LMDB */ + + /* + * Configure the logging system. + * + * Do this after changing UID to make sure that any log + * files specified in named.conf get created by the + * unprivileged user, not root. + */ + if (named_g_logstderr) { + const cfg_obj_t *logobj = NULL; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "not using config file logging " + "statement for logging due to " + "-g option"); + + (void)cfg_map_get(config, "logging", &logobj); + if (logobj != NULL) { + result = named_logconfig(NULL, logobj); + if (result != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "checking logging configuration " + "failed: %s", + isc_result_totext(result)); + goto cleanup; + } + } + } else { + const cfg_obj_t *logobj = NULL; + + isc_logconfig_create(named_g_lctx, &logc); + + logobj = NULL; + (void)cfg_map_get(config, "logging", &logobj); + if (logobj != NULL) { + CHECKM(named_logconfig(logc, logobj), + "configuring logging"); + } else { + named_log_setdefaultchannels(logc); + named_log_setdefaultsslkeylogfile(logc); + CHECKM(named_log_setunmatchedcategory(logc), + "setting up default 'category unmatched'"); + CHECKM(named_log_setdefaultcategory(logc), + "setting up default 'category default'"); + } + + isc_logconfig_use(named_g_lctx, logc); + logc = NULL; + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "now using logging configuration from " + "config file"); + } + + /* + * Set the default value of the query logging flag depending + * whether a "queries" category has been defined. This is + * a disgusting hack, but we need to do this for BIND 8 + * compatibility. + */ + if (first_time) { + const cfg_obj_t *logobj = NULL; + const cfg_obj_t *categories = NULL; + + obj = NULL; + if (named_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) { + ns_server_setoption(server->sctx, NS_SERVER_LOGQUERIES, + cfg_obj_asboolean(obj)); + } else { + (void)cfg_map_get(config, "logging", &logobj); + if (logobj != NULL) { + (void)cfg_map_get(logobj, "category", + &categories); + } + if (categories != NULL) { + for (element = cfg_list_first(categories); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *catobj; + const char *str; + + obj = cfg_listelt_value(element); + catobj = cfg_tuple_get(obj, "name"); + str = cfg_obj_asstring(catobj); + if (strcasecmp(str, "queries") == 0) { + ns_server_setoption( + server->sctx, + NS_SERVER_LOGQUERIES, + true); + } + } + } + } + } + + obj = NULL; + if (options != NULL && + cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS) + { + named_g_memstatistics = cfg_obj_asboolean(obj); + } else { + named_g_memstatistics = + ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0); + } + + obj = NULL; + if (named_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS) + { + named_main_setmemstats(cfg_obj_asstring(obj)); + } else if (named_g_memstatistics) { + named_main_setmemstats("named.memstats"); + } else { + named_main_setmemstats(NULL); + } + + obj = NULL; + result = named_config_get(maps, "statistics-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = named_config_get(maps, "dump-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = named_config_get(maps, "secroots-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->secrootsfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = named_config_get(maps, "recursing-file", &obj); + INSIST(result == ISC_R_SUCCESS); + CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)), + "strdup"); + + obj = NULL; + result = named_config_get(maps, "version", &obj); + if (result == ISC_R_SUCCESS) { + CHECKM(setoptstring(server, &server->version, obj), "strdup"); + server->version_set = true; + } else { + server->version_set = false; + } + + obj = NULL; + result = named_config_get(maps, "hostname", &obj); + if (result == ISC_R_SUCCESS) { + CHECKM(setoptstring(server, &server->hostname, obj), "strdup"); + server->hostname_set = true; + } else { + server->hostname_set = false; + } + + obj = NULL; + result = named_config_get(maps, "server-id", &obj); + server->sctx->usehostname = false; + if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { + /* The parser translates "hostname" to true */ + server->sctx->usehostname = true; + result = ns_server_setserverid(server->sctx, NULL); + } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + /* Found a quoted string */ + result = ns_server_setserverid(server->sctx, + cfg_obj_asstring(obj)); + } else { + result = ns_server_setserverid(server->sctx, NULL); + } + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + obj = NULL; + result = named_config_get(maps, "flush-zones-on-shutdown", &obj); + if (result == ISC_R_SUCCESS) { + server->flushonshutdown = cfg_obj_asboolean(obj); + } else { + server->flushonshutdown = false; + } + + obj = NULL; + result = named_config_get(maps, "answer-cookie", &obj); + INSIST(result == ISC_R_SUCCESS); + server->sctx->answercookie = cfg_obj_asboolean(obj); + + obj = NULL; + result = named_config_get(maps, "cookie-algorithm", &obj); + INSIST(result == ISC_R_SUCCESS); + if (strcasecmp(cfg_obj_asstring(obj), "siphash24") == 0) { + server->sctx->cookiealg = ns_cookiealg_siphash24; + } else if (strcasecmp(cfg_obj_asstring(obj), "aes") == 0) { + server->sctx->cookiealg = ns_cookiealg_aes; + } else { + UNREACHABLE(); + } + + obj = NULL; + result = named_config_get(maps, "cookie-secret", &obj); + if (result == ISC_R_SUCCESS) { + const char *str; + bool first = true; + isc_buffer_t b; + unsigned int usedlength; + unsigned int expectedlength; + + for (element = cfg_list_first(obj); element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + + if (first) { + memset(server->sctx->secret, 0, + sizeof(server->sctx->secret)); + isc_buffer_init(&b, server->sctx->secret, + sizeof(server->sctx->secret)); + result = isc_hex_decodestring(str, &b); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOSPACE) + { + goto cleanup; + } + first = false; + } else { + altsecret = isc_mem_get(server->sctx->mctx, + sizeof(*altsecret)); + isc_buffer_init(&b, altsecret->secret, + sizeof(altsecret->secret)); + result = isc_hex_decodestring(str, &b); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOSPACE) + { + isc_mem_put(server->sctx->mctx, + altsecret, + sizeof(*altsecret)); + goto cleanup; + } + ISC_LIST_INITANDAPPEND(altsecrets, altsecret, + link); + } + + usedlength = isc_buffer_usedlength(&b); + switch (server->sctx->cookiealg) { + case ns_cookiealg_siphash24: + expectedlength = ISC_SIPHASH24_KEY_LENGTH; + if (usedlength != expectedlength) { + CHECKM(ISC_R_RANGE, "SipHash-2-4 " + "cookie-secret " + "must be 128 bits"); + } + break; + case ns_cookiealg_aes: + expectedlength = ISC_AES128_KEYLENGTH; + if (usedlength != expectedlength) { + CHECKM(ISC_R_RANGE, "AES cookie-secret " + "must be 128 bits"); + } + break; + } + } + } else { + isc_nonce_buf(server->sctx->secret, + sizeof(server->sctx->secret)); + } + + /* + * Swap altsecrets lists. + */ + tmpaltsecrets = server->sctx->altsecrets; + server->sctx->altsecrets = altsecrets; + altsecrets = tmpaltsecrets; + + (void)named_server_loadnta(server); + +#ifdef USE_DNSRPS + /* + * Start and connect to the DNS Response Policy Service + * daemon, dnsrpzd, for each view that uses DNSRPS. + */ + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + result = dns_dnsrps_connect(view->rpzs); + if (result != ISC_R_SUCCESS) { + view = NULL; + goto cleanup; + } + } +#endif /* ifdef USE_DNSRPS */ + + result = ISC_R_SUCCESS; + +cleanup: + if (logc != NULL) { + isc_logconfig_destroy(&logc); + } + + if (v4portset != NULL) { + isc_portset_destroy(named_g_mctx, &v4portset); + } + + if (v6portset != NULL) { + isc_portset_destroy(named_g_mctx, &v6portset); + } + + if (conf_parser != NULL) { + if (config != NULL) { + cfg_obj_destroy(conf_parser, &config); + } + cfg_parser_destroy(&conf_parser); + } + + if (bindkeys_parser != NULL) { + if (bindkeys != NULL) { + cfg_obj_destroy(bindkeys_parser, &bindkeys); + } + cfg_parser_destroy(&bindkeys_parser); + } + + if (view != NULL) { + dns_view_detach(&view); + } + + if (kasp != NULL) { + dns_kasp_detach(&kasp); + } + + ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link); + + /* + * This cleans up either the old production view list + * or our temporary list depending on whether they + * were swapped above or not. + */ + for (view = ISC_LIST_HEAD(viewlist); view != NULL; view = view_next) { + view_next = ISC_LIST_NEXT(view, link); + ISC_LIST_UNLINK(viewlist, view, link); + if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) + { + dns_view_setviewrevert(view); + (void)dns_zt_apply(view->zonetable, isc_rwlocktype_read, + false, NULL, removed, view); + } + dns_view_detach(&view); + } + + /* + * Same cleanup for kasp list. + */ + for (kasp = ISC_LIST_HEAD(kasplist); kasp != NULL; kasp = kasp_next) { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(kasplist, kasp, link); + dns_kasp_detach(&kasp); + } + + /* Same cleanup for cache list. */ + while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) { + ISC_LIST_UNLINK(cachelist, nsc, link); + dns_cache_detach(&nsc->cache); + isc_mem_put(server->mctx, nsc, sizeof(*nsc)); + } + + /* Cleanup for altsecrets list. */ + while ((altsecret = ISC_LIST_HEAD(altsecrets)) != NULL) { + ISC_LIST_UNLINK(altsecrets, altsecret, link); + isc_mem_put(server->sctx->mctx, altsecret, sizeof(*altsecret)); + } + + /* + * Record the time of most recent configuration + */ + tresult = isc_time_now(&named_g_configtime); + if (tresult != ISC_R_SUCCESS) { + named_main_earlyfatal("isc_time_now() failed: %s", + isc_result_totext(result)); + } + + /* Relinquish exclusive access to configuration data. */ + if (exclusive) { + isc_task_endexclusive(server->task); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "load_configuration: %s", isc_result_totext(result)); + + return (result); +} + +static isc_result_t +view_loaded(void *arg) { + isc_result_t result; + ns_zoneload_t *zl = (ns_zoneload_t *)arg; + + /* + * Force zone maintenance. Do this after loading + * so that we know when we need to force AXFR of + * secondary zones whose master files are missing. + * + * We use the zoneload reference counter to let us + * know when all views are finished. + */ + if (isc_refcount_decrement(&zl->refs) == 1) { + named_server_t *server = zl->server; + bool reconfig = zl->reconfig; + dns_view_t *view = NULL; + + isc_refcount_destroy(&zl->refs); + isc_mem_put(server->mctx, zl, sizeof(*zl)); + + /* + * To maintain compatibility with log parsing tools that might + * be looking for this string after "rndc reconfig", we keep it + * as it is + */ + if (reconfig) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "any newly configured zones are now " + "loaded"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE, + "all zones loaded"); + } + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (view->managed_keys != NULL) { + result = dns_zone_synckeyzone( + view->managed_keys); + if (result != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, + DNS_LOGCATEGORY_DNSSEC, + DNS_LOGMODULE_DNSSEC, + ISC_LOG_ERROR, + "failed to initialize " + "managed-keys for view %s " + "(%s): DNSSEC validation is " + "at risk", + view->name, + isc_result_totext(result)); + } + } + } + + CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr), + "forcing zone maintenance"); + + named_os_started(); + +#ifdef HAVE_FIPS_MODE + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE, + "FIPS mode is %s", + FIPS_mode() ? "enabled" : "disabled"); +#endif /* ifdef HAVE_FIPS_MODE */ + atomic_store(&server->reload_status, NAMED_RELOAD_DONE); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE, + "running"); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +load_zones(named_server_t *server, bool init, bool reconfig) { + isc_result_t result; + isc_taskmgr_t *taskmgr = dns_zonemgr_gettaskmgr(server->zonemgr); + ns_zoneload_t *zl = NULL; + dns_view_t *view = NULL; + + zl = isc_mem_get(server->mctx, sizeof(*zl)); + zl->server = server; + zl->reconfig = reconfig; + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_refcount_init(&zl->refs, 1); + + /* + * Schedule zones to be loaded from disk. + */ + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (view->managed_keys != NULL) { + result = dns_zone_load(view->managed_keys, false); + if (result != ISC_R_SUCCESS && + result != DNS_R_UPTODATE && + result != DNS_R_CONTINUE) + { + goto cleanup; + } + } + if (view->redirect != NULL) { + result = dns_zone_load(view->redirect, false); + if (result != ISC_R_SUCCESS && + result != DNS_R_UPTODATE && + result != DNS_R_CONTINUE) + { + goto cleanup; + } + } + + /* + * 'dns_view_asyncload' calls view_loaded if there are no + * zones. + */ + isc_refcount_increment(&zl->refs); + result = dns_view_asyncload(view, reconfig, view_loaded, zl); + if (result != ISC_R_SUCCESS) { + isc_refcount_decrement1(&zl->refs); + goto cleanup; + } + } + +cleanup: + if (isc_refcount_decrement(&zl->refs) == 1) { + isc_refcount_destroy(&zl->refs); + isc_mem_put(server->mctx, zl, sizeof(*zl)); + } + + if (init) { + /* + * If we're setting up the server for the first time, set + * the task manager into privileged mode; this ensures + * that no other tasks will begin to run until after zone + * loading is complete. We won't return from exclusive mode + * until the loading is finished; we can then drop out of + * privileged mode. + * + * We do *not* want to do this in the case of reload or + * reconfig, as loading a large zone could cause the server + * to be inactive for too long a time. + */ + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); + isc_task_endexclusive(server->task); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_normal); + } else { + isc_task_endexclusive(server->task); + } + + return (result); +} + +static void +run_server(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + named_server_t *server = (named_server_t *)event->ev_arg; + dns_geoip_databases_t *geoip; + + INSIST(task == server->task); + + isc_event_free(&event); + + CHECKFATAL(dns_dispatchmgr_create(named_g_mctx, named_g_netmgr, + &named_g_dispatchmgr), + "creating dispatch manager"); + + dns_dispatchmgr_setstats(named_g_dispatchmgr, server->resolverstats); + +#if defined(HAVE_GEOIP2) + geoip = named_g_geoip; +#else /* if defined(HAVE_GEOIP2) */ + geoip = NULL; +#endif /* if defined(HAVE_GEOIP2) */ + + CHECKFATAL(ns_interfacemgr_create(named_g_mctx, server->sctx, + named_g_taskmgr, named_g_timermgr, + named_g_netmgr, named_g_dispatchmgr, + server->task, geoip, named_g_cpus, + true, &server->interfacemgr), + "creating interface manager"); + + CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, + interface_timer_tick, server, + &server->interface_timer), + "creating interface timer"); + + CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, + heartbeat_timer_tick, server, + &server->heartbeat_timer), + "creating heartbeat timer"); + + CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, tat_timer_tick, + server, &server->tat_timer), + "creating trust anchor telemetry timer"); + + CHECKFATAL(isc_timer_create(named_g_timermgr, isc_timertype_inactive, + NULL, NULL, server->task, pps_timer_tick, + server, &server->pps_timer), + "creating pps timer"); + + CHECKFATAL( + cfg_parser_create(named_g_mctx, named_g_lctx, &named_g_parser), + "creating default configuration parser"); + + CHECKFATAL(cfg_parser_create(named_g_mctx, named_g_lctx, + &named_g_addparser), + "creating additional configuration parser"); + + CHECKFATAL(load_configuration(named_g_conffile, server, true), + "loading configuration"); + + CHECKFATAL(load_zones(server, true, false), "loading zones"); +#ifdef ENABLE_AFL + named_g_run_done = true; +#endif /* ifdef ENABLE_AFL */ +} + +void +named_server_flushonshutdown(named_server_t *server, bool flush) { + REQUIRE(NAMED_SERVER_VALID(server)); + + server->flushonshutdown = flush; +} + +static void +shutdown_server(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_view_t *view, *view_next = NULL; + dns_kasp_t *kasp, *kasp_next = NULL; + named_server_t *server = (named_server_t *)event->ev_arg; + bool flush = server->flushonshutdown; + named_cache_t *nsc; + + UNUSED(task); + INSIST(task == server->task); + + /* + * We need to shutdown the interface before going + * exclusive (which would pause the netmgr). + */ + ns_interfacemgr_shutdown(server->interfacemgr); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, "shutting down%s", + flush ? ": flushing changes" : ""); + + named_statschannels_shutdown(server); + named_controls_shutdown(server->controls); + end_reserved_dispatches(server, true); + cleanup_session_key(server, server->mctx); + + if (named_g_aclconfctx != NULL) { + cfg_aclconfctx_detach(&named_g_aclconfctx); + } + + cfg_obj_destroy(named_g_parser, &named_g_config); + cfg_parser_destroy(&named_g_parser); + cfg_parser_destroy(&named_g_addparser); + + (void)named_server_saventa(server); + + for (kasp = ISC_LIST_HEAD(server->kasplist); kasp != NULL; + kasp = kasp_next) + { + kasp_next = ISC_LIST_NEXT(kasp, link); + ISC_LIST_UNLINK(server->kasplist, kasp, link); + dns_kasp_detach(&kasp); + } + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = view_next) + { + view_next = ISC_LIST_NEXT(view, link); + ISC_LIST_UNLINK(server->viewlist, view, link); + if (flush) { + dns_view_flushanddetach(&view); + } else { + dns_view_detach(&view); + } + } + + /* + * Shut down all dyndb instances. + */ + dns_dyndb_cleanup(true); + + while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) { + ISC_LIST_UNLINK(server->cachelist, nsc, link); + dns_cache_detach(&nsc->cache); + isc_mem_put(server->mctx, nsc, sizeof(*nsc)); + } + + isc_timer_destroy(&server->interface_timer); + isc_timer_destroy(&server->heartbeat_timer); + isc_timer_destroy(&server->pps_timer); + isc_timer_destroy(&server->tat_timer); + + ns_interfacemgr_detach(&server->interfacemgr); + + dns_dispatchmgr_detach(&named_g_dispatchmgr); + + dns_zonemgr_shutdown(server->zonemgr); + + if (named_g_sessionkey != NULL) { + dns_tsigkey_detach(&named_g_sessionkey); + dns_name_free(&named_g_sessionkeyname, server->mctx); + } +#if defined(HAVE_GEOIP2) + named_geoip_shutdown(); +#endif /* HAVE_GEOIP2 */ + + dns_db_detach(&server->in_roothints); + + isc_task_endexclusive(server->task); + + isc_task_detach(&server->task); + + isc_event_free(&event); +} + +/*% + * Find a view that matches the source and destination addresses of a query. + */ +static isc_result_t +get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr, + dns_message_t *message, dns_aclenv_t *env, + isc_result_t *sigresult, dns_view_t **viewp) { + dns_view_t *view; + + REQUIRE(message != NULL); + REQUIRE(sigresult != NULL); + REQUIRE(viewp != NULL && *viewp == NULL); + + for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (message->rdclass == view->rdclass || + message->rdclass == dns_rdataclass_any) + { + const dns_name_t *tsig = NULL; + + *sigresult = dns_message_rechecksig(message, view); + if (*sigresult == ISC_R_SUCCESS) { + dns_tsigkey_t *tsigkey; + + tsigkey = message->tsigkey; + tsig = dns_tsigkey_identity(tsigkey); + } + + if (dns_acl_allowed(srcaddr, tsig, view->matchclients, + env) && + dns_acl_allowed(destaddr, tsig, + view->matchdestinations, env) && + !(view->matchrecursiveonly && + (message->flags & DNS_MESSAGEFLAG_RD) == 0)) + { + dns_view_attach(view, viewp); + return (ISC_R_SUCCESS); + } + } + } + + return (ISC_R_NOTFOUND); +} + +void +named_server_create(isc_mem_t *mctx, named_server_t **serverp) { + isc_result_t result; + named_server_t *server = isc_mem_get(mctx, sizeof(*server)); + + *server = (named_server_t){ + .mctx = mctx, + .statsfile = isc_mem_strdup(mctx, "named.stats"), + .bindkeysfile = isc_mem_strdup(mctx, named_g_defaultbindkeys), + .dumpfile = isc_mem_strdup(mctx, "named_dump.db"), + .secrootsfile = isc_mem_strdup(mctx, "named.secroots"), + .recfile = isc_mem_strdup(mctx, "named.recursing"), + }; + +#ifdef USE_DNSRPS + CHECKFATAL(dns_dnsrps_server_create(), "initializing RPZ service " + "interface"); +#endif /* ifdef USE_DNSRPS */ + + /* Initialize server data structures. */ + ISC_LIST_INIT(server->kasplist); + ISC_LIST_INIT(server->viewlist); + + /* Must be first. */ + CHECKFATAL(dst_lib_init(named_g_mctx, named_g_engine), "initializing " + "DST"); + + CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL, + &server->in_roothints), + "setting up root hints"); + + atomic_init(&server->reload_status, NAMED_RELOAD_IN_PROGRESS); + + /* + * Setup the server task, which is responsible for coordinating + * startup and shutdown of the server, as well as all exclusive + * tasks. + */ + CHECKFATAL(isc_task_create_bound(named_g_taskmgr, 0, &server->task, 0), + "creating server task"); + isc_task_setname(server->task, "server", server); + isc_taskmgr_setexcltask(named_g_taskmgr, server->task); + + CHECKFATAL(ns_server_create(mctx, get_matching_view, &server->sctx), + "creating server context"); + +#if defined(HAVE_GEOIP2) + /* + * GeoIP must be initialized before the interface + * manager (which includes the ACL environment) + * is created. + */ + named_geoip_init(); +#endif /* HAVE_GEOIP2 */ + +#ifdef ENABLE_AFL + server->sctx->fuzztype = named_g_fuzz_type; + server->sctx->fuzznotify = named_fuzz_notify; +#endif /* ifdef ENABLE_AFL */ + + CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server), + "isc_task_onshutdown"); + CHECKFATAL( + isc_app_onrun(named_g_mctx, server->task, run_server, server), + "isc_app_onrun"); + + CHECKFATAL(dns_zonemgr_create(named_g_mctx, named_g_taskmgr, + named_g_timermgr, named_g_netmgr, + &server->zonemgr), + "dns_zonemgr_create"); + CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000), "dns_zonemgr_" + "setsize"); + + CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats, + isc_sockstatscounter_max), + "isc_stats_create"); + isc_nm_setstats(named_g_netmgr, server->sockstats); + + CHECKFATAL(isc_stats_create(named_g_mctx, &server->zonestats, + dns_zonestatscounter_max), + "dns_stats_create (zone)"); + + CHECKFATAL(isc_stats_create(named_g_mctx, &server->resolverstats, + dns_resstatscounter_max), + "dns_stats_create (resolver)"); + + CHECKFATAL(named_controls_create(server, &server->controls), + "named_controls_create"); + + ISC_LIST_INIT(server->dispatches); + + ISC_LIST_INIT(server->statschannels); + + ISC_LIST_INIT(server->cachelist); + + server->magic = NAMED_SERVER_MAGIC; + + *serverp = server; +} + +void +named_server_destroy(named_server_t **serverp) { + named_server_t *server = *serverp; + REQUIRE(NAMED_SERVER_VALID(server)); + +#ifdef HAVE_DNSTAP + if (server->dtenv != NULL) { + dns_dt_detach(&server->dtenv); + } +#endif /* HAVE_DNSTAP */ + +#ifdef USE_DNSRPS + dns_dnsrps_server_destroy(); +#endif /* ifdef USE_DNSRPS */ + + named_controls_destroy(&server->controls); + + isc_stats_detach(&server->zonestats); + isc_stats_detach(&server->sockstats); + isc_stats_detach(&server->resolverstats); + + if (server->sctx != NULL) { + ns_server_detach(&server->sctx); + } + + isc_mem_free(server->mctx, server->statsfile); + isc_mem_free(server->mctx, server->bindkeysfile); + isc_mem_free(server->mctx, server->dumpfile); + isc_mem_free(server->mctx, server->secrootsfile); + isc_mem_free(server->mctx, server->recfile); + + if (server->version != NULL) { + isc_mem_free(server->mctx, server->version); + } + if (server->hostname != NULL) { + isc_mem_free(server->mctx, server->hostname); + } + if (server->lockfile != NULL) { + isc_mem_free(server->mctx, server->lockfile); + } + + if (server->zonemgr != NULL) { + dns_zonemgr_detach(&server->zonemgr); + } + + dst_lib_destroy(); + + INSIST(ISC_LIST_EMPTY(server->kasplist)); + INSIST(ISC_LIST_EMPTY(server->viewlist)); + INSIST(ISC_LIST_EMPTY(server->cachelist)); + + if (server->tlsctx_server_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_server_cache); + } + + if (server->tlsctx_client_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_client_cache); + } + + server->magic = 0; + isc_mem_put(server->mctx, server, sizeof(*server)); + *serverp = NULL; +} + +static void +fatal(named_server_t *server, const char *msg, isc_result_t result) { + if (server != NULL && server->task != NULL) { + /* + * Prevent races between the OpenSSL on_exit registered + * function and any other OpenSSL calls from other tasks + * by requesting exclusive access to the task manager. + */ + (void)isc_task_beginexclusive(server->task); + } + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_CRITICAL, "%s: %s", msg, + isc_result_totext(result)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_CRITICAL, + "exiting (due to fatal error)"); + named_os_shutdown(); + exit(1); +} + +static void +start_reserved_dispatches(named_server_t *server) { + REQUIRE(NAMED_SERVER_VALID(server)); + + server->dispatchgen++; +} + +static void +end_reserved_dispatches(named_server_t *server, bool all) { + named_dispatch_t *dispatch, *nextdispatch; + + REQUIRE(NAMED_SERVER_VALID(server)); + + for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; + dispatch = nextdispatch) + { + nextdispatch = ISC_LIST_NEXT(dispatch, link); + if (!all && server->dispatchgen == dispatch->dispatchgen) { + continue; + } + ISC_LIST_UNLINK(server->dispatches, dispatch, link); + dns_dispatch_detach(&dispatch->dispatch); + isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); + } +} + +void +named_add_reserved_dispatch(named_server_t *server, + const isc_sockaddr_t *addr) { + named_dispatch_t *dispatch; + in_port_t port; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_result_t result; + + REQUIRE(NAMED_SERVER_VALID(server)); + + port = isc_sockaddr_getport(addr); + if (port == 0 || port >= 1024) { + return; + } + + for (dispatch = ISC_LIST_HEAD(server->dispatches); dispatch != NULL; + dispatch = ISC_LIST_NEXT(dispatch, link)) + { + if (isc_sockaddr_equal(&dispatch->addr, addr)) { + break; + } + } + if (dispatch != NULL) { + dispatch->dispatchgen = server->dispatchgen; + return; + } + + dispatch = isc_mem_get(server->mctx, sizeof(*dispatch)); + + dispatch->addr = *addr; + dispatch->dispatchgen = server->dispatchgen; + dispatch->dispatch = NULL; + + result = dns_dispatch_createudp(named_g_dispatchmgr, &dispatch->addr, + &dispatch->dispatch); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link); + + return; + +cleanup: + isc_mem_put(server->mctx, dispatch, sizeof(*dispatch)); + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "unable to create dispatch for reserved port %s: %s", + addrbuf, isc_result_totext(result)); +} + +static isc_result_t +loadconfig(named_server_t *server) { + isc_result_t result; + start_reserved_dispatches(server); + result = load_configuration(named_g_conffile, server, false); + if (result == ISC_R_SUCCESS) { + end_reserved_dispatches(server, false); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "reloading configuration succeeded"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "reloading configuration failed: %s", + isc_result_totext(result)); + atomic_store(&server->reload_status, NAMED_RELOAD_FAILED); + } + + return (result); +} + +static isc_result_t +reload(named_server_t *server) { + isc_result_t result; + + atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS); + + CHECK(loadconfig(server)); + + result = load_zones(server, false, false); + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "reloading zones succeeded"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "reloading zones failed: %s", + isc_result_totext(result)); + atomic_store(&server->reload_status, NAMED_RELOAD_FAILED); + } +cleanup: + return (result); +} + +/* + * Handle a reload event (from SIGHUP). + */ +static void +named_server_reload(isc_task_t *task, isc_event_t *event) { + named_server_t *server = (named_server_t *)event->ev_sender; + + INSIST(task == server->task); + UNUSED(task); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "received SIGHUP signal to reload zones"); + (void)reload(server); + + isc_event_free(&event); +} + +void +named_server_reloadwanted(named_server_t *server) { + isc_event_t *event = isc_event_allocate( + named_g_mctx, server, NAMED_EVENT_RELOAD, named_server_reload, + NULL, sizeof(isc_event_t)); + isc_task_send(server->task, &event); +} + +void +named_server_scan_interfaces(named_server_t *server) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1), + "automatic interface rescan"); + + ns_interfacemgr_scan(server->interfacemgr, true, false); +} + +/* + * Get the next token from lexer 'lex'. + * + * NOTE: the token value for string tokens always uses the same pointer + * value. Multiple calls to this function on the same lexer will always + * return either that value (lex->data) or NULL. It is necessary to copy + * the token into local storage if it needs to be referenced after the next + * call to next_token(). + */ +static char * +next_token(isc_lex_t *lex, isc_buffer_t **text) { + isc_result_t result; + isc_token_t token; + + token.type = isc_tokentype_unknown; + result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF | ISC_LEXOPT_QSTRING, + &token); + + switch (result) { + case ISC_R_NOMORE: + (void)isc_lex_close(lex); + break; + case ISC_R_SUCCESS: + if (token.type == isc_tokentype_eof) { + (void)isc_lex_close(lex); + } + break; + case ISC_R_NOSPACE: + if (text != NULL) { + (void)putstr(text, "token too large"); + (void)putnull(text); + } + return (NULL); + default: + if (text != NULL) { + (void)putstr(text, isc_result_totext(result)); + (void)putnull(text); + } + return (NULL); + } + + if (token.type == isc_tokentype_string || + token.type == isc_tokentype_qstring) + { + return (token.value.as_textregion.base); + } + + return (NULL); +} + +/* + * Find the zone specified in the control channel command, if any. + * If a zone is specified, point '*zonep' at it, otherwise + * set '*zonep' to NULL, and f 'zonename' is not NULL, copy + * the zone name into it (N.B. 'zonename' must have space to hold + * a full DNS name). + * + * If 'zonetxt' is set, the caller has already pulled a token + * off the command line that is to be used as the zone name. (This + * is sometimes done when it's necessary to check for an optional + * argument before the zone name, as in "rndc sync [-clean] zone".) + */ +static isc_result_t +zone_from_args(named_server_t *server, isc_lex_t *lex, const char *zonetxt, + dns_zone_t **zonep, char *zonename, isc_buffer_t **text, + bool skip) { + char *ptr; + char *classtxt; + const char *viewtxt = NULL; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result; + dns_view_t *view = NULL; + dns_rdataclass_t rdclass; + char problem[DNS_NAME_FORMATSIZE + 500] = ""; + char zonebuf[DNS_NAME_FORMATSIZE]; + bool redirect = false; + + REQUIRE(zonep != NULL && *zonep == NULL); + + if (skip) { + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + } + + /* Look for the zone name. */ + if (zonetxt == NULL) { + zonetxt = next_token(lex, text); + } + if (zonetxt == NULL) { + return (ISC_R_SUCCESS); + } + + /* Copy zonetxt because it'll be overwritten by next_token() */ + /* To locate a zone named "-redirect" use "-redirect." */ + if (strcmp(zonetxt, "-redirect") == 0) { + redirect = true; + strlcpy(zonebuf, ".", DNS_NAME_FORMATSIZE); + } else { + strlcpy(zonebuf, zonetxt, DNS_NAME_FORMATSIZE); + } + if (zonename != NULL) { + strlcpy(zonename, redirect ? "." : zonetxt, + DNS_NAME_FORMATSIZE); + } + + name = dns_fixedname_initname(&fname); + CHECK(dns_name_fromstring(name, zonebuf, 0, NULL)); + + /* Look for the optional class name. */ + classtxt = next_token(lex, text); + if (classtxt != NULL) { + isc_textregion_t r; + r.base = classtxt; + r.length = strlen(classtxt); + CHECK(dns_rdataclass_fromtext(&rdclass, &r)); + + /* Look for the optional view name. */ + viewtxt = next_token(lex, text); + } else { + rdclass = dns_rdataclass_in; + } + + if (viewtxt == NULL) { + if (redirect) { + result = dns_viewlist_find(&server->viewlist, + "_default", + dns_rdataclass_in, &view); + if (result != ISC_R_SUCCESS || view->redirect == NULL) { + result = ISC_R_NOTFOUND; + snprintf(problem, sizeof(problem), + "redirect zone not found in " + "_default view"); + } else { + dns_zone_attach(view->redirect, zonep); + result = ISC_R_SUCCESS; + } + } else { + result = dns_viewlist_findzone(&server->viewlist, name, + (classtxt == NULL), + rdclass, zonep); + if (result == ISC_R_NOTFOUND) { + snprintf(problem, sizeof(problem), + "no matching zone '%s' in any view", + zonebuf); + } else if (result == ISC_R_MULTIPLE) { + snprintf(problem, sizeof(problem), + "zone '%s' was found in multiple " + "views", + zonebuf); + } + } + } else { + result = dns_viewlist_find(&server->viewlist, viewtxt, rdclass, + &view); + if (result != ISC_R_SUCCESS) { + snprintf(problem, sizeof(problem), + "no matching view '%s'", viewtxt); + goto report; + } + + if (redirect) { + if (view->redirect != NULL) { + dns_zone_attach(view->redirect, zonep); + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOTFOUND; + } + } else { + result = dns_zt_find(view->zonetable, name, 0, NULL, + zonep); + } + if (result != ISC_R_SUCCESS) { + snprintf(problem, sizeof(problem), + "no matching zone '%s' in view '%s'", zonebuf, + viewtxt); + } + } + + /* Partial match? */ + if (result != ISC_R_SUCCESS && *zonep != NULL) { + dns_zone_detach(zonep); + } + if (result == DNS_R_PARTIALMATCH) { + result = ISC_R_NOTFOUND; + } +report: + if (result != ISC_R_SUCCESS) { + isc_result_t tresult; + + tresult = putstr(text, problem); + if (tresult == ISC_R_SUCCESS) { + (void)putnull(text); + } + } + +cleanup: + if (view != NULL) { + dns_view_detach(&view); + } + + return (result); +} + +/* + * Act on a "retransfer" command from the command channel. + */ +isc_result_t +named_server_retransfercommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zone_t *raw = NULL; + dns_zonetype_t type; + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &zone, NULL, text, true); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (zone == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + dns_zone_detach(&zone); + dns_zone_attach(raw, &zone); + dns_zone_detach(&raw); + } + type = dns_zone_gettype(zone); + if (type == dns_zone_secondary || type == dns_zone_mirror || + type == dns_zone_stub || + (type == dns_zone_redirect && + dns_zone_getredirecttype(zone) == dns_zone_secondary)) + { + dns_zone_forcereload(zone); + } else { + (void)putstr(text, "retransfer: inappropriate zone type: "); + (void)putstr(text, dns_zonetype_name(type)); + if (type == dns_zone_redirect) { + type = dns_zone_getredirecttype(zone); + (void)putstr(text, "("); + (void)putstr(text, dns_zonetype_name(type)); + (void)putstr(text, ")"); + } + (void)putnull(text); + result = ISC_R_FAILURE; + } + dns_zone_detach(&zone); + return (result); +} + +/* + * Act on a "reload" command from the command channel. + */ +isc_result_t +named_server_reloadcommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zonetype_t type; + const char *msg = NULL; + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &zone, NULL, text, true); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (zone == NULL) { + result = reload(server); + if (result == ISC_R_SUCCESS) { + msg = "server reload successful"; + } + } else { + type = dns_zone_gettype(zone); + if (type == dns_zone_secondary || type == dns_zone_mirror || + type == dns_zone_stub) + { + dns_zone_refresh(zone); + dns_zone_detach(&zone); + msg = "zone refresh queued"; + } else { + result = dns_zone_load(zone, false); + dns_zone_detach(&zone); + switch (result) { + case ISC_R_SUCCESS: + msg = "zone reload successful"; + break; + case DNS_R_CONTINUE: + msg = "zone reload queued"; + result = ISC_R_SUCCESS; + break; + case DNS_R_UPTODATE: + msg = "zone reload up-to-date"; + result = ISC_R_SUCCESS; + break; + default: + /* failure message will be generated by rndc */ + break; + } + } + } + if (msg != NULL) { + (void)putstr(text, msg); + (void)putnull(text); + } + return (result); +} + +/* + * Act on a "reconfig" command from the command channel. + */ +isc_result_t +named_server_reconfigcommand(named_server_t *server) { + isc_result_t result; + atomic_store(&server->reload_status, NAMED_RELOAD_IN_PROGRESS); + + CHECK(loadconfig(server)); + + result = load_zones(server, false, true); + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "scheduled loading new zones"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "loading new zones failed: %s", + isc_result_totext(result)); + atomic_store(&server->reload_status, NAMED_RELOAD_FAILED); + } +cleanup: + return (result); +} + +/* + * Act on a "notify" command from the command channel. + */ +isc_result_t +named_server_notifycommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_zone_t *zone = NULL; + const char msg[] = "zone notify queued"; + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &zone, NULL, text, true); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (zone == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + dns_zone_notify(zone); + dns_zone_detach(&zone); + (void)putstr(text, msg); + (void)putnull(text); + + return (ISC_R_SUCCESS); +} + +/* + * Act on a "refresh" command from the command channel. + */ +isc_result_t +named_server_refreshcommand(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_zone_t *zone = NULL, *raw = NULL; + const char msg1[] = "zone refresh queued"; + const char msg2[] = "not a secondary, mirror, or stub zone"; + dns_zonetype_t type; + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &zone, NULL, text, true); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (zone == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + dns_zone_detach(&zone); + dns_zone_attach(raw, &zone); + dns_zone_detach(&raw); + } + + type = dns_zone_gettype(zone); + if (type == dns_zone_secondary || type == dns_zone_mirror || + type == dns_zone_stub) + { + dns_zone_refresh(zone); + dns_zone_detach(&zone); + (void)putstr(text, msg1); + (void)putnull(text); + return (ISC_R_SUCCESS); + } + + dns_zone_detach(&zone); + (void)putstr(text, msg2); + (void)putnull(text); + return (ISC_R_FAILURE); +} + +isc_result_t +named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) { + bool prev, value; + char *ptr; + + /* Skip the command name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + prev = ns_server_getoption(server->sctx, NS_SERVER_LOGQUERIES); + + ptr = next_token(lex, NULL); + if (ptr == NULL) { + value = !prev; + } else if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") || + !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) + { + value = true; + } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") || + !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) + { + value = false; + } else { + return (DNS_R_SYNTAX); + } + + if (value == prev) { + return (ISC_R_SUCCESS); + } + + ns_server_setoption(server->sctx, NS_SERVER_LOGQUERIES, value); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "query logging is now %s", value ? "on" : "off"); + return (ISC_R_SUCCESS); +} + +static isc_result_t +listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, + ns_listenlist_t **target) { + isc_result_t result; + const cfg_listelt_t *element; + ns_listenlist_t *dlist = NULL; + + REQUIRE(target != NULL && *target == NULL); + + result = ns_listenlist_create(mctx, &dlist); + if (result != ISC_R_SUCCESS) { + return (result); + } + + for (element = cfg_list_first(listenlist); element != NULL; + element = cfg_list_next(element)) + { + ns_listenelt_t *delt = NULL; + const cfg_obj_t *listener = cfg_listelt_value(element); + result = listenelt_fromconfig(listener, config, actx, mctx, + family, tlsctx_cache, &delt); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + ISC_LIST_APPEND(dlist->elts, delt, link); + } + *target = dlist; + return (ISC_R_SUCCESS); + +cleanup: + ns_listenlist_detach(&dlist); + return (result); +} + +static const cfg_obj_t * +find_maplist(const cfg_obj_t *config, const char *listname, const char *name) { + isc_result_t result; + const cfg_obj_t *maplist = NULL; + const cfg_listelt_t *elt = NULL; + + REQUIRE(config != NULL); + REQUIRE(name != NULL); + + result = cfg_map_get(config, listname, &maplist); + if (result != ISC_R_SUCCESS) { + return (NULL); + } + + for (elt = cfg_list_first(maplist); elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *map = cfg_listelt_value(elt); + if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) == + 0) + { + return (map); + } + } + + return (NULL); +} + +/* + * Create a listen list from the corresponding configuration + * data structure. + */ +static isc_result_t +listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, + ns_listenelt_t **target) { + isc_result_t result; + const cfg_obj_t *ltup = NULL; + const cfg_obj_t *tlsobj = NULL, *httpobj = NULL; + const cfg_obj_t *portobj = NULL; + const cfg_obj_t *http_server = NULL; + in_port_t port = 0; + const char *key = NULL, *cert = NULL, *ca_file = NULL, + *dhparam_file = NULL, *ciphers = NULL; + bool tls_prefer_server_ciphers = false, + tls_prefer_server_ciphers_set = false; + bool tls_session_tickets = false, tls_session_tickets_set = false; + bool do_tls = false, no_tls = false, http = false; + ns_listenelt_t *delt = NULL; + uint32_t tls_protos = 0; + ns_listen_tls_params_t tls_params = { 0 }; + const char *tlsname = NULL; + + REQUIRE(target != NULL && *target == NULL); + + ltup = cfg_tuple_get(listener, "tuple"); + RUNTIME_CHECK(ltup != NULL); + + tlsobj = cfg_tuple_get(ltup, "tls"); + if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { + tlsname = cfg_obj_asstring(tlsobj); + + if (strcasecmp(tlsname, "none") == 0) { + no_tls = true; + } else if (strcasecmp(tlsname, "ephemeral") == 0) { + do_tls = true; + } else { + const cfg_obj_t *keyobj = NULL, *certobj = NULL, + *ca_obj = NULL, *dhparam_obj = NULL; + const cfg_obj_t *tlsmap = NULL; + const cfg_obj_t *tls_proto_list = NULL; + const cfg_obj_t *ciphers_obj = NULL; + const cfg_obj_t *prefer_server_ciphers_obj = NULL; + const cfg_obj_t *session_tickets_obj = NULL; + + do_tls = true; + + tlsmap = find_maplist(config, "tls", tlsname); + if (tlsmap == NULL) { + cfg_obj_log(tlsobj, named_g_lctx, ISC_LOG_ERROR, + "tls '%s' is not defined", + cfg_obj_asstring(tlsobj)); + return (ISC_R_FAILURE); + } + + CHECK(cfg_map_get(tlsmap, "key-file", &keyobj)); + key = cfg_obj_asstring(keyobj); + + CHECK(cfg_map_get(tlsmap, "cert-file", &certobj)); + cert = cfg_obj_asstring(certobj); + + if (cfg_map_get(tlsmap, "ca-file", &ca_obj) == + ISC_R_SUCCESS) + { + ca_file = cfg_obj_asstring(ca_obj); + } + + if (cfg_map_get(tlsmap, "protocols", &tls_proto_list) == + ISC_R_SUCCESS) + { + const cfg_listelt_t *proto = NULL; + INSIST(tls_proto_list != NULL); + for (proto = cfg_list_first(tls_proto_list); + proto != 0; proto = cfg_list_next(proto)) + { + const cfg_obj_t *tls_proto_obj = + cfg_listelt_value(proto); + const char *tls_sver = + cfg_obj_asstring(tls_proto_obj); + const isc_tls_protocol_version_t ver = + isc_tls_protocol_name_to_version( + tls_sver); + + INSIST(ver != + ISC_TLS_PROTO_VER_UNDEFINED); + INSIST(isc_tls_protocol_supported(ver)); + tls_protos |= ver; + } + } + + if (cfg_map_get(tlsmap, "dhparam-file", &dhparam_obj) == + ISC_R_SUCCESS) + { + dhparam_file = cfg_obj_asstring(dhparam_obj); + } + + if (cfg_map_get(tlsmap, "ciphers", &ciphers_obj) == + ISC_R_SUCCESS) + { + ciphers = cfg_obj_asstring(ciphers_obj); + } + + if (cfg_map_get(tlsmap, "prefer-server-ciphers", + &prefer_server_ciphers_obj) == + ISC_R_SUCCESS) + { + tls_prefer_server_ciphers = cfg_obj_asboolean( + prefer_server_ciphers_obj); + tls_prefer_server_ciphers_set = true; + } + + if (cfg_map_get(tlsmap, "session-tickets", + &session_tickets_obj) == ISC_R_SUCCESS) + { + tls_session_tickets = + cfg_obj_asboolean(session_tickets_obj); + tls_session_tickets_set = true; + } + } + } + + tls_params = (ns_listen_tls_params_t){ + .name = tlsname, + .key = key, + .cert = cert, + .ca_file = ca_file, + .protocols = tls_protos, + .dhparam_file = dhparam_file, + .ciphers = ciphers, + .prefer_server_ciphers = tls_prefer_server_ciphers, + .prefer_server_ciphers_set = tls_prefer_server_ciphers_set, + .session_tickets = tls_session_tickets, + .session_tickets_set = tls_session_tickets_set + }; + + httpobj = cfg_tuple_get(ltup, "http"); + if (httpobj != NULL && cfg_obj_isstring(httpobj)) { + const char *httpname = cfg_obj_asstring(httpobj); + + if (!do_tls && !no_tls) { + return (ISC_R_FAILURE); + } + + http_server = find_maplist(config, "http", httpname); + if (http_server == NULL && strcasecmp(httpname, "default") != 0) + { + cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, + "http '%s' is not defined", + cfg_obj_asstring(httpobj)); + return (ISC_R_FAILURE); + } + + http = true; + } + + portobj = cfg_tuple_get(ltup, "port"); + if (!cfg_obj_isuint32(portobj)) { + if (http && do_tls) { + if (named_g_httpsport != 0) { + port = named_g_httpsport; + } else { + result = named_config_getport( + config, "https-port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } else if (http && !do_tls) { + if (named_g_httpport != 0) { + port = named_g_httpport; + } else { + result = named_config_getport( + config, "http-port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } else if (do_tls) { + if (named_g_tlsport != 0) { + port = named_g_tlsport; + } else { + result = named_config_getport( + config, "tls-port", &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } else { + if (named_g_port != 0) { + port = named_g_port; + } else { + result = named_config_getport(config, "port", + &port); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } + } else { + if (cfg_obj_asuint32(portobj) >= UINT16_MAX) { + return (ISC_R_RANGE); + } + port = (in_port_t)cfg_obj_asuint32(portobj); + } + +#ifdef HAVE_LIBNGHTTP2 + if (http) { + CHECK(listenelt_http(http_server, family, do_tls, &tls_params, + tlsctx_cache, port, mctx, &delt)); + } +#endif /* HAVE_LIBNGHTTP2 */ + + if (!http) { + CHECK(ns_listenelt_create(mctx, port, NULL, family, do_tls, + &tls_params, tlsctx_cache, &delt)); + } + + result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config, + named_g_lctx, actx, mctx, 0, family, + &delt->acl); + if (result != ISC_R_SUCCESS) { + ns_listenelt_destroy(delt); + return (result); + } + *target = delt; + +cleanup: + return (result); +} + +#ifdef HAVE_LIBNGHTTP2 +static isc_result_t +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, + isc_mem_t *mctx, ns_listenelt_t **target) { + isc_result_t result = ISC_R_SUCCESS; + ns_listenelt_t *delt = NULL; + char **endpoints = NULL; + const cfg_obj_t *eplist = NULL; + const cfg_listelt_t *elt = NULL; + size_t len = 1, i = 0; + uint32_t max_clients = named_g_http_listener_clients; + uint32_t max_streams = named_g_http_streams_per_conn; + + REQUIRE(target != NULL && *target == NULL); + + if (tls) { + INSIST(tls_params != NULL); + INSIST((tls_params->key == NULL) == (tls_params->cert == NULL)); + } + + if (port == 0) { + port = tls ? named_g_httpsport : named_g_httpport; + } + + /* + * If "default" was used, we set up the default endpoint + * of "/dns-query". + */ + if (http != NULL) { + const cfg_obj_t *cfg_max_clients = NULL; + const cfg_obj_t *cfg_max_streams = NULL; + + if (cfg_map_get(http, "endpoints", &eplist) == ISC_R_SUCCESS) { + INSIST(eplist != NULL); + len = cfg_list_length(eplist, false); + } + + if (cfg_map_get(http, "listener-clients", &cfg_max_clients) == + ISC_R_SUCCESS) + { + INSIST(cfg_max_clients != NULL); + max_clients = cfg_obj_asuint32(cfg_max_clients); + } + + if (cfg_map_get(http, "streams-per-connection", + &cfg_max_streams) == ISC_R_SUCCESS) + { + INSIST(cfg_max_streams != NULL); + max_streams = cfg_obj_asuint32(cfg_max_streams); + } + } + + endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len); + + if (http != NULL && eplist != NULL) { + for (elt = cfg_list_first(eplist); elt != NULL; + elt = cfg_list_next(elt)) + { + const cfg_obj_t *ep = cfg_listelt_value(elt); + const char *path = cfg_obj_asstring(ep); + endpoints[i++] = isc_mem_strdup(mctx, path); + } + } else { + endpoints[i++] = isc_mem_strdup(mctx, ISC_NM_HTTP_DEFAULT_PATH); + } + + INSIST(i == len); + + result = ns_listenelt_create_http(mctx, port, NULL, family, tls, + tls_params, tlsctx_cache, endpoints, + len, max_clients, max_streams, &delt); + if (result != ISC_R_SUCCESS) { + goto error; + } + + *target = delt; + + return (result); +error: + if (delt != NULL) { + ns_listenelt_destroy(delt); + } + return (result); +} +#endif /* HAVE_LIBNGHTTP2 */ + +isc_result_t +named_server_dumpstats(named_server_t *server) { + isc_result_t result; + FILE *fp = NULL; + + CHECKMF(isc_stdio_open(server->statsfile, "a", &fp), + "could not open statistics dump file", server->statsfile); + + result = named_stats_dump(server, fp); + +cleanup: + if (fp != NULL) { + (void)isc_stdio_close(fp); + } + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpstats complete"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dumpstats failed: %s", + isc_result_totext(result)); + } + return (result); +} + +static isc_result_t +add_zone_tolist(dns_zone_t *zone, void *uap) { + struct dumpcontext *dctx = uap; + struct zonelistentry *zle; + + zle = isc_mem_get(dctx->mctx, sizeof *zle); + zle->zone = NULL; + dns_zone_attach(zone, &zle->zone); + ISC_LINK_INIT(zle, link); + ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link); + return (ISC_R_SUCCESS); +} + +static isc_result_t +add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) { + struct viewlistentry *vle; + isc_result_t result = ISC_R_SUCCESS; + + /* + * Prevent duplicate views. + */ + for (vle = ISC_LIST_HEAD(dctx->viewlist); vle != NULL; + vle = ISC_LIST_NEXT(vle, link)) + { + if (vle->view == view) { + return (ISC_R_SUCCESS); + } + } + + vle = isc_mem_get(dctx->mctx, sizeof *vle); + vle->view = NULL; + dns_view_attach(view, &vle->view); + ISC_LINK_INIT(vle, link); + ISC_LIST_INIT(vle->zonelist); + ISC_LIST_APPEND(dctx->viewlist, vle, link); + if (dctx->dumpzones) { + result = dns_zt_apply(view->zonetable, isc_rwlocktype_read, + true, NULL, add_zone_tolist, dctx); + } + return (result); +} + +static void +dumpcontext_destroy(struct dumpcontext *dctx) { + struct viewlistentry *vle; + struct zonelistentry *zle; + + vle = ISC_LIST_HEAD(dctx->viewlist); + while (vle != NULL) { + ISC_LIST_UNLINK(dctx->viewlist, vle, link); + zle = ISC_LIST_HEAD(vle->zonelist); + while (zle != NULL) { + ISC_LIST_UNLINK(vle->zonelist, zle, link); + dns_zone_detach(&zle->zone); + isc_mem_put(dctx->mctx, zle, sizeof *zle); + zle = ISC_LIST_HEAD(vle->zonelist); + } + dns_view_detach(&vle->view); + isc_mem_put(dctx->mctx, vle, sizeof *vle); + vle = ISC_LIST_HEAD(dctx->viewlist); + } + if (dctx->version != NULL) { + dns_db_closeversion(dctx->db, &dctx->version, false); + } + if (dctx->db != NULL) { + dns_db_detach(&dctx->db); + } + if (dctx->cache != NULL) { + dns_db_detach(&dctx->cache); + } + if (dctx->task != NULL) { + isc_task_detach(&dctx->task); + } + if (dctx->fp != NULL) { + (void)isc_stdio_close(dctx->fp); + } + if (dctx->mdctx != NULL) { + dns_dumpctx_detach(&dctx->mdctx); + } + isc_mem_put(dctx->mctx, dctx, sizeof *dctx); +} + +static void +dumpdone(void *arg, isc_result_t result) { + struct dumpcontext *dctx = arg; + char buf[1024 + 32]; + const dns_master_style_t *style; + + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + if (dctx->mdctx != NULL) { + dns_dumpctx_detach(&dctx->mdctx); + } + if (dctx->view == NULL) { + dctx->view = ISC_LIST_HEAD(dctx->viewlist); + if (dctx->view == NULL) { + goto done; + } + INSIST(dctx->zone == NULL); + } else { + goto resume; + } +nextview: + fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name); +resume: + if (dctx->dumpcache && dns_view_iscacheshared(dctx->view->view)) { + fprintf(dctx->fp, ";\n; Cache of view '%s' is shared as '%s'\n", + dctx->view->view->name, + dns_cache_getname(dctx->view->view->cache)); + } else if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) + { + if (dctx->dumpexpired) { + style = &dns_master_style_cache_with_expired; + } else { + style = &dns_master_style_cache; + } + /* start cache dump */ + if (dctx->view->view->cachedb != NULL) { + dns_db_attach(dctx->view->view->cachedb, &dctx->cache); + } + if (dctx->cache != NULL) { + fprintf(dctx->fp, + ";\n; Cache dump of view '%s' (cache %s)\n;\n", + dctx->view->view->name, + dns_cache_getname(dctx->view->view->cache)); + result = dns_master_dumptostreamasync( + dctx->mctx, dctx->cache, NULL, style, dctx->fp, + dctx->task, dumpdone, dctx, &dctx->mdctx); + if (result == DNS_R_CONTINUE) { + return; + } + if (result == ISC_R_NOTIMPLEMENTED) { + fprintf(dctx->fp, "; %s\n", + isc_result_totext(result)); + } else if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + } + + if ((dctx->dumpadb || dctx->dumpbad || dctx->dumpfail) && + dctx->cache == NULL && dctx->view->view->cachedb != NULL) + { + dns_db_attach(dctx->view->view->cachedb, &dctx->cache); + } + + if (dctx->cache != NULL) { + if (dctx->dumpadb) { + dns_adb_dump(dctx->view->view->adb, dctx->fp); + } + if (dctx->dumpbad) { + dns_resolver_printbadcache(dctx->view->view->resolver, + dctx->fp); + } + if (dctx->dumpfail) { + dns_badcache_print(dctx->view->view->failcache, + "SERVFAIL cache", dctx->fp); + } + dns_db_detach(&dctx->cache); + } + if (dctx->dumpzones) { + style = &dns_master_style_full; + nextzone: + if (dctx->version != NULL) { + dns_db_closeversion(dctx->db, &dctx->version, false); + } + if (dctx->db != NULL) { + dns_db_detach(&dctx->db); + } + if (dctx->zone == NULL) { + dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist); + } else { + dctx->zone = ISC_LIST_NEXT(dctx->zone, link); + } + if (dctx->zone != NULL) { + /* start zone dump */ + dns_zone_name(dctx->zone->zone, buf, sizeof(buf)); + fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf); + result = dns_zone_getdb(dctx->zone->zone, &dctx->db); + if (result != ISC_R_SUCCESS) { + fprintf(dctx->fp, "; %s\n", + isc_result_totext(result)); + goto nextzone; + } + dns_db_currentversion(dctx->db, &dctx->version); + result = dns_master_dumptostreamasync( + dctx->mctx, dctx->db, dctx->version, style, + dctx->fp, dctx->task, dumpdone, dctx, + &dctx->mdctx); + if (result == DNS_R_CONTINUE) { + return; + } + if (result == ISC_R_NOTIMPLEMENTED) { + fprintf(dctx->fp, "; %s\n", + isc_result_totext(result)); + result = ISC_R_SUCCESS; + POST(result); + goto nextzone; + } + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + } + if (dctx->view != NULL) { + dctx->view = ISC_LIST_NEXT(dctx->view, link); + if (dctx->view != NULL) { + goto nextview; + } + } +done: + fprintf(dctx->fp, "; Dump complete\n"); + result = isc_stdio_flush(dctx->fp); + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpdb complete"); + } +cleanup: + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dumpdb failed: %s", isc_result_totext(result)); + } + dumpcontext_destroy(dctx); +} + +isc_result_t +named_server_dumpdb(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + struct dumpcontext *dctx = NULL; + dns_view_t *view; + isc_result_t result; + char *ptr; + const char *sep; + bool found; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + dctx = isc_mem_get(server->mctx, sizeof(*dctx)); + + dctx->mctx = server->mctx; + dctx->dumpcache = true; + dctx->dumpadb = true; + dctx->dumpbad = true; + dctx->dumpexpired = false; + dctx->dumpfail = true; + dctx->dumpzones = false; + dctx->fp = NULL; + ISC_LIST_INIT(dctx->viewlist); + dctx->view = NULL; + dctx->zone = NULL; + dctx->cache = NULL; + dctx->mdctx = NULL; + dctx->db = NULL; + dctx->cache = NULL; + dctx->task = NULL; + dctx->version = NULL; + isc_task_attach(server->task, &dctx->task); + + CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp), + "could not open dump file", server->dumpfile); + + ptr = next_token(lex, NULL); + sep = (ptr == NULL) ? "" : ": "; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpdb started%s%s", sep, (ptr != NULL) ? ptr : ""); + + if (ptr != NULL && strcmp(ptr, "-all") == 0) { + /* also dump zones */ + dctx->dumpzones = true; + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) { + /* this is the default */ + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-expired") == 0) { + /* this is the same as -cache but includes expired data */ + dctx->dumpexpired = true; + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) { + /* only dump zones, suppress caches */ + dctx->dumpadb = false; + dctx->dumpbad = false; + dctx->dumpcache = false; + dctx->dumpfail = false; + dctx->dumpzones = true; + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-adb") == 0) { + /* only dump adb, suppress other caches */ + dctx->dumpbad = false; + dctx->dumpcache = false; + dctx->dumpfail = false; + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-bad") == 0) { + /* only dump badcache, suppress other caches */ + dctx->dumpadb = false; + dctx->dumpcache = false; + dctx->dumpfail = false; + ptr = next_token(lex, NULL); + } else if (ptr != NULL && strcmp(ptr, "-fail") == 0) { + /* only dump servfail cache, suppress other caches */ + dctx->dumpadb = false; + dctx->dumpbad = false; + dctx->dumpcache = false; + ptr = next_token(lex, NULL); + } + +nextview: + found = false; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (ptr != NULL && strcmp(view->name, ptr) != 0) { + continue; + } + found = true; + CHECK(add_view_tolist(dctx, view)); + } + if (ptr != NULL) { + if (!found) { + CHECK(putstr(text, "view '")); + CHECK(putstr(text, ptr)); + CHECK(putstr(text, "' not found")); + CHECK(putnull(text)); + result = ISC_R_NOTFOUND; + dumpdone(dctx, result); + return (result); + } + ptr = next_token(lex, NULL); + if (ptr != NULL) { + goto nextview; + } + } + dumpdone(dctx, ISC_R_SUCCESS); + return (ISC_R_SUCCESS); + +cleanup: + dumpcontext_destroy(dctx); + return (result); +} + +isc_result_t +named_server_dumpsecroots(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + dns_view_t *view; + dns_keytable_t *secroots = NULL; + dns_ntatable_t *ntatable = NULL; + isc_result_t result; + char *ptr; + FILE *fp = NULL; + isc_time_t now; + char tbuf[64]; + unsigned int used = isc_buffer_usedlength(*text); + bool first = true; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* "-" here means print the output instead of dumping to file */ + ptr = next_token(lex, text); + if (ptr != NULL && strcmp(ptr, "-") == 0) { + ptr = next_token(lex, text); + } else { + result = isc_stdio_open(server->secrootsfile, "w", &fp); + if (result != ISC_R_SUCCESS) { + (void)putstr(text, "could not open "); + (void)putstr(text, server->secrootsfile); + CHECKMF(result, "could not open secroots dump file", + server->secrootsfile); + } + } + + TIME_NOW(&now); + isc_time_formattimestamp(&now, tbuf, sizeof(tbuf)); + CHECK(putstr(text, "secure roots as of ")); + CHECK(putstr(text, tbuf)); + CHECK(putstr(text, ":\n")); + used = isc_buffer_usedlength(*text); + + do { + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (ptr != NULL && strcmp(view->name, ptr) != 0) { + continue; + } + if (secroots != NULL) { + dns_keytable_detach(&secroots); + } + result = dns_view_getsecroots(view, &secroots); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + continue; + } + if (first || used != isc_buffer_usedlength(*text)) { + CHECK(putstr(text, "\n")); + first = false; + } + CHECK(putstr(text, " Start view ")); + CHECK(putstr(text, view->name)); + CHECK(putstr(text, "\n Secure roots:\n\n")); + used = isc_buffer_usedlength(*text); + CHECK(dns_keytable_totext(secroots, text)); + + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + result = dns_view_getntatable(view, &ntatable); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + continue; + } + if (used != isc_buffer_usedlength(*text)) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, " Negative trust anchors:\n\n")); + used = isc_buffer_usedlength(*text); + CHECK(dns_ntatable_totext(ntatable, NULL, text)); + } + + if (ptr != NULL) { + ptr = next_token(lex, text); + } + } while (ptr != NULL); + +cleanup: + if (secroots != NULL) { + dns_keytable_detach(&secroots); + } + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + + if (fp != NULL) { + if (used != isc_buffer_usedlength(*text)) { + (void)putstr(text, "\n"); + } + fprintf(fp, "%.*s", (int)isc_buffer_usedlength(*text), + (char *)isc_buffer_base(*text)); + isc_buffer_clear(*text); + (void)isc_stdio_close(fp); + } else if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumpsecroots complete"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dumpsecroots failed: %s", + isc_result_totext(result)); + } + return (result); +} + +isc_result_t +named_server_dumprecursing(named_server_t *server) { + FILE *fp = NULL; + dns_view_t *view; + isc_result_t result; + + CHECKMF(isc_stdio_open(server->recfile, "w", &fp), + "could not open dump file", server->recfile); + fprintf(fp, ";\n; Recursing Queries\n;\n"); + ns_interfacemgr_dumprecursing(fp, server->interfacemgr); + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + fprintf(fp, ";\n; Active fetch domains [view: %s]\n;\n", + view->name); + dns_resolver_dumpfetches(view->resolver, isc_statsformat_file, + fp); + } + + fprintf(fp, "; Dump complete\n"); + +cleanup: + if (fp != NULL) { + result = isc_stdio_close(fp); + } + if (result == ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumprecursing complete"); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "dumprecursing failed: %s", + isc_result_totext(result)); + } + return (result); +} + +isc_result_t +named_server_setdebuglevel(named_server_t *server, isc_lex_t *lex) { + char *ptr; + char *endp; + long newlevel; + + UNUSED(server); + + /* Skip the command name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Look for the new level name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + if (named_g_debuglevel < 99) { + named_g_debuglevel++; + } + } else { + newlevel = strtol(ptr, &endp, 10); + if (*endp != '\0' || newlevel < 0 || newlevel > 99) { + return (ISC_R_RANGE); + } + named_g_debuglevel = (unsigned int)newlevel; + } + isc_log_setdebuglevel(named_g_lctx, named_g_debuglevel); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "debug level is now %u", named_g_debuglevel); + return (ISC_R_SUCCESS); +} + +isc_result_t +named_server_validation(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + char *ptr; + dns_view_t *view; + bool changed = false; + isc_result_t result; + bool enable = true, set = true, first = true; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Find out what we are to do. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") || + !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) + { + enable = true; + } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") || + !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) + { + enable = false; + } else if (!strcasecmp(ptr, "check") || !strcasecmp(ptr, "status")) { + set = false; + } else { + return (DNS_R_SYNTAX); + } + + /* Look for the view name. */ + ptr = next_token(lex, text); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if ((ptr != NULL && strcasecmp(ptr, view->name) != 0) || + strcasecmp("_bind", view->name) == 0) + { + continue; + } + + if (set) { + CHECK(dns_view_flushcache(view, false)); + view->enablevalidation = enable; + changed = true; + } else { + if (!first) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, "DNSSEC validation is ")); + CHECK(putstr(text, view->enablevalidation + ? "enabled" + : "disabled")); + CHECK(putstr(text, " (view ")); + CHECK(putstr(text, view->name)); + CHECK(putstr(text, ")")); + first = false; + } + } + CHECK(putnull(text)); + + if (!set) { + result = ISC_R_SUCCESS; + } else if (changed) { + result = ISC_R_SUCCESS; + } else { + result = ISC_R_FAILURE; + } +cleanup: + isc_task_endexclusive(server->task); + return (result); +} + +isc_result_t +named_server_flushcache(named_server_t *server, isc_lex_t *lex) { + char *ptr; + dns_view_t *view; + bool flushed; + bool found; + isc_result_t result; + named_cache_t *nsc; + + /* Skip the command name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Look for the view name. */ + ptr = next_token(lex, NULL); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + flushed = true; + found = false; + + /* + * Flushing a cache is tricky when caches are shared by multiple views. + * We first identify which caches should be flushed in the local cache + * list, flush these caches, and then update other views that refer to + * the flushed cache DB. + */ + if (ptr != NULL) { + /* + * Mark caches that need to be flushed. This is an O(#view^2) + * operation in the very worst case, but should be normally + * much more lightweight because only a few (most typically just + * one) views will match. + */ + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (strcasecmp(ptr, view->name) != 0) { + continue; + } + found = true; + for (nsc = ISC_LIST_HEAD(server->cachelist); + nsc != NULL; nsc = ISC_LIST_NEXT(nsc, link)) + { + if (nsc->cache == view->cache) { + break; + } + } + INSIST(nsc != NULL); + nsc->needflush = true; + } + } else { + found = true; + } + + /* Perform flush */ + for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; + nsc = ISC_LIST_NEXT(nsc, link)) + { + if (ptr != NULL && !nsc->needflush) { + continue; + } + nsc->needflush = true; + result = dns_view_flushcache(nsc->primaryview, false); + if (result != ISC_R_SUCCESS) { + flushed = false; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "flushing cache in view '%s' failed: %s", + nsc->primaryview->name, + isc_result_totext(result)); + } + } + + /* + * Fix up views that share a flushed cache: let the views update the + * cache DB they're referring to. This could also be an expensive + * operation, but should typically be marginal: the inner loop is only + * necessary for views that share a cache, and if there are many such + * views the number of shared cache should normally be small. + * A worst case is that we have n views and n/2 caches, each shared by + * two views. Then this will be a O(n^2/4) operation. + */ + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (!dns_view_iscacheshared(view)) { + continue; + } + for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; + nsc = ISC_LIST_NEXT(nsc, link)) + { + if (!nsc->needflush || nsc->cache != view->cache) { + continue; + } + result = dns_view_flushcache(view, true); + if (result != ISC_R_SUCCESS) { + flushed = false; + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "fixing cache in view '%s' " + "failed: %s", + view->name, isc_result_totext(result)); + } + } + } + + /* Cleanup the cache list. */ + for (nsc = ISC_LIST_HEAD(server->cachelist); nsc != NULL; + nsc = ISC_LIST_NEXT(nsc, link)) + { + nsc->needflush = false; + } + + if (flushed && found) { + if (ptr != NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "flushing cache in view '%s' succeeded", + ptr); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "flushing caches in all views succeeded"); + } + result = ISC_R_SUCCESS; + } else { + if (!found) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "flushing cache in view '%s' failed: " + "view not found", + ptr); + result = ISC_R_NOTFOUND; + } else { + result = ISC_R_FAILURE; + } + } + isc_task_endexclusive(server->task); + return (result); +} + +isc_result_t +named_server_flushnode(named_server_t *server, isc_lex_t *lex, bool tree) { + char *ptr, *viewname; + char target[DNS_NAME_FORMATSIZE]; + dns_view_t *view; + bool flushed; + bool found; + isc_result_t result; + isc_buffer_t b; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Skip the command name. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Find the domain name to flush. */ + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + strlcpy(target, ptr, DNS_NAME_FORMATSIZE); + isc_buffer_constinit(&b, target, strlen(target)); + isc_buffer_add(&b, strlen(target)); + name = dns_fixedname_initname(&fixed); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* Look for the view name. */ + viewname = next_token(lex, NULL); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + flushed = true; + found = false; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewname != NULL && strcasecmp(viewname, view->name) != 0) { + continue; + } + found = true; + /* + * It's a little inefficient to try flushing name for all views + * if some of the views share a single cache. But since the + * operation is lightweight we prefer simplicity here. + */ + result = dns_view_flushnode(view, name, tree); + if (result != ISC_R_SUCCESS) { + flushed = false; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "flushing %s '%s' in cache view '%s' " + "failed: %s", + tree ? "tree" : "name", target, + view->name, isc_result_totext(result)); + } + } + if (flushed && found) { + if (viewname != NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "flushing %s '%s' in cache view '%s' " + "succeeded", + tree ? "tree" : "name", target, viewname); + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "flushing %s '%s' in all cache views " + "succeeded", + tree ? "tree" : "name", target); + } + result = ISC_R_SUCCESS; + } else { + if (!found) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "flushing %s '%s' in cache view '%s' " + "failed: view not found", + tree ? "tree" : "name", target, viewname); + } + result = ISC_R_FAILURE; + } + isc_task_endexclusive(server->task); + return (result); +} + +isc_result_t +named_server_status(named_server_t *server, isc_buffer_t **text) { + isc_result_t result; + unsigned int zonecount, xferrunning, xferdeferred, soaqueries; + unsigned int automatic; + const char *ob = "", *cb = "", *alt = ""; + char boottime[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char configtime[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char line[1024], hostname[256]; + named_reload_t reload_status; + + REQUIRE(text != NULL); + + if (named_g_server->version_set) { + ob = " ("; + cb = ")"; + if (named_g_server->version == NULL) { + alt = "version.bind/txt/ch disabled"; + } else { + alt = named_g_server->version; + } + } + zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY); + xferrunning = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_XFERRUNNING); + xferdeferred = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_XFERDEFERRED); + soaqueries = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_SOAQUERY); + automatic = dns_zonemgr_getcount(server->zonemgr, + DNS_ZONESTATE_AUTOMATIC); + + isc_time_formathttptimestamp(&named_g_boottime, boottime, + sizeof(boottime)); + isc_time_formathttptimestamp(&named_g_configtime, configtime, + sizeof(configtime)); + + snprintf(line, sizeof(line), "version: %s%s %s%s%s\n", + PACKAGE_STRING, PACKAGE_DESCRIPTION, PACKAGE_SRCID, ob, alt, + cb); + CHECK(putstr(text, line)); + + if (gethostname(hostname, sizeof(hostname)) == 0) { + strlcpy(hostname, "localhost", sizeof(hostname)); + } + snprintf(line, sizeof(line), "running on %s: %s\n", hostname, + named_os_uname()); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "boot time: %s\n", boottime); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "last configured: %s\n", configtime); + CHECK(putstr(text, line)); + + if (named_g_chrootdir != NULL) { + snprintf(line, sizeof(line), "configuration file: %s (%s%s)\n", + named_g_conffile, named_g_chrootdir, named_g_conffile); + } else { + snprintf(line, sizeof(line), "configuration file: %s\n", + named_g_conffile); + } + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "CPUs found: %u\n", named_g_cpus_detected); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "worker threads: %u\n", named_g_cpus); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "UDP listeners per interface: %u\n", + named_g_udpdisp); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "number of zones: %u (%u automatic)\n", + zonecount, automatic); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "debug level: %u\n", named_g_debuglevel); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "xfers running: %u\n", xferrunning); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "xfers deferred: %u\n", xferdeferred); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "soa queries in progress: %u\n", + soaqueries); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "query logging is %s\n", + ns_server_getoption(server->sctx, NS_SERVER_LOGQUERIES) + ? "ON" + : "OFF"); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "recursive clients: %u/%u/%u\n", + isc_quota_getused(&server->sctx->recursionquota), + isc_quota_getsoft(&server->sctx->recursionquota), + isc_quota_getmax(&server->sctx->recursionquota)); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "tcp clients: %u/%u\n", + isc_quota_getused(&server->sctx->tcpquota), + isc_quota_getmax(&server->sctx->tcpquota)); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "TCP high-water: %u\n", + (unsigned)ns_stats_get_counter(server->sctx->nsstats, + ns_statscounter_tcphighwater)); + CHECK(putstr(text, line)); + + reload_status = atomic_load(&server->reload_status); + if (reload_status != NAMED_RELOAD_DONE) { + snprintf(line, sizeof(line), "reload/reconfig %s\n", + (reload_status == NAMED_RELOAD_FAILED + ? "failed" + : "in progress")); + CHECK(putstr(text, line)); + } + + CHECK(putstr(text, "server is up and running")); + CHECK(putnull(text)); + + return (ISC_R_SUCCESS); +cleanup: + return (result); +} + +isc_result_t +named_server_testgen(isc_lex_t *lex, isc_buffer_t **text) { + isc_result_t result; + char *ptr; + unsigned long count; + unsigned long i; + const unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + ptr = next_token(lex, text); + if (ptr == NULL) { + count = 26; + } else { + count = strtoul(ptr, NULL, 10); + } + + CHECK(isc_buffer_reserve(text, count)); + for (i = 0; i < count; i++) { + CHECK(putuint8(text, chars[i % (sizeof(chars) - 1)])); + } + + CHECK(putnull(text)); + +cleanup: + return (result); +} + +static isc_result_t +delete_keynames(dns_tsig_keyring_t *ring, char *target, + unsigned int *foundkeys) { + char namestr[DNS_NAME_FORMATSIZE]; + isc_result_t result; + dns_rbtnodechain_t chain; + dns_name_t foundname; + dns_fixedname_t fixedorigin; + dns_name_t *origin; + dns_rbtnode_t *node; + dns_tsigkey_t *tkey; + + dns_name_init(&foundname, NULL); + origin = dns_fixedname_initname(&fixedorigin); + +again: + dns_rbtnodechain_init(&chain); + result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); + if (result == ISC_R_NOTFOUND) { + dns_rbtnodechain_invalidate(&chain); + return (ISC_R_SUCCESS); + } + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + dns_rbtnodechain_invalidate(&chain); + return (result); + } + + for (;;) { + node = NULL; + dns_rbtnodechain_current(&chain, &foundname, origin, &node); + tkey = node->data; + + if (tkey != NULL) { + if (!tkey->generated) { + goto nextkey; + } + + dns_name_format(&tkey->name, namestr, sizeof(namestr)); + if (strcmp(namestr, target) == 0) { + (*foundkeys)++; + dns_rbtnodechain_invalidate(&chain); + (void)dns_rbt_deletename(ring->keys, + &tkey->name, false); + goto again; + } + } + + nextkey: + result = dns_rbtnodechain_next(&chain, &foundname, origin); + if (result == ISC_R_NOMORE) { + break; + } + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + dns_rbtnodechain_invalidate(&chain); + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +named_server_tsigdelete(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_view_t *view; + unsigned int foundkeys = 0; + char *ptr, *viewname; + char target[DNS_NAME_FORMATSIZE]; + char fbuf[16]; + + REQUIRE(text != NULL); + + (void)next_token(lex, text); /* skip command name */ + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + strlcpy(target, ptr, DNS_NAME_FORMATSIZE); + + viewname = next_token(lex, text); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewname == NULL || strcmp(view->name, viewname) == 0) { + RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write); + result = delete_keynames(view->dynamickeys, target, + &foundkeys); + RWUNLOCK(&view->dynamickeys->lock, + isc_rwlocktype_write); + if (result != ISC_R_SUCCESS) { + isc_task_endexclusive(server->task); + return (result); + } + } + } + isc_task_endexclusive(server->task); + + snprintf(fbuf, sizeof(fbuf), "%u", foundkeys); + + CHECK(putstr(text, fbuf)); + CHECK(putstr(text, " tsig keys deleted.")); + CHECK(putnull(text)); + +cleanup: + return (result); +} + +static isc_result_t +list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t **text, + unsigned int *foundkeys) { + char namestr[DNS_NAME_FORMATSIZE]; + char creatorstr[DNS_NAME_FORMATSIZE]; + isc_result_t result; + dns_rbtnodechain_t chain; + dns_name_t foundname; + dns_fixedname_t fixedorigin; + dns_name_t *origin; + dns_rbtnode_t *node; + dns_tsigkey_t *tkey; + const char *viewname; + + if (view != NULL) { + viewname = view->name; + } else { + viewname = "(global)"; + } + + dns_name_init(&foundname, NULL); + origin = dns_fixedname_initname(&fixedorigin); + dns_rbtnodechain_init(&chain); + result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin); + if (result == ISC_R_NOTFOUND) { + dns_rbtnodechain_invalidate(&chain); + return (ISC_R_SUCCESS); + } + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + dns_rbtnodechain_invalidate(&chain); + return (result); + } + + for (;;) { + node = NULL; + dns_rbtnodechain_current(&chain, &foundname, origin, &node); + tkey = node->data; + + if (tkey != NULL) { + dns_name_format(&tkey->name, namestr, sizeof(namestr)); + if (tkey->generated) { + dns_name_format(tkey->creator, creatorstr, + sizeof(creatorstr)); + if (*foundkeys != 0) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, "view \"")); + CHECK(putstr(text, viewname)); + CHECK(putstr(text, "\"; type \"dynamic\"; key " + "\"")); + CHECK(putstr(text, namestr)); + CHECK(putstr(text, "\"; creator \"")); + CHECK(putstr(text, creatorstr)); + CHECK(putstr(text, "\";")); + } else { + if (*foundkeys != 0) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, "view \"")); + CHECK(putstr(text, viewname)); + CHECK(putstr(text, "\"; type \"static\"; key " + "\"")); + CHECK(putstr(text, namestr)); + CHECK(putstr(text, "\";")); + } + (*foundkeys)++; + } + result = dns_rbtnodechain_next(&chain, &foundname, origin); + if (result == ISC_R_NOMORE || result == DNS_R_NEWORIGIN) { + break; + } + } + + return (ISC_R_SUCCESS); +cleanup: + dns_rbtnodechain_invalidate(&chain); + return (result); +} + +isc_result_t +named_server_tsiglist(named_server_t *server, isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_view_t *view; + unsigned int foundkeys = 0; + + REQUIRE(text != NULL); + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + RWLOCK(&view->statickeys->lock, isc_rwlocktype_read); + result = list_keynames(view, view->statickeys, text, + &foundkeys); + RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read); + if (result != ISC_R_SUCCESS) { + return (result); + } + RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); + result = list_keynames(view, view->dynamickeys, text, + &foundkeys); + RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + if (foundkeys == 0) { + CHECK(putstr(text, "no tsig keys found.")); + } + + if (isc_buffer_usedlength(*text) > 0) { + CHECK(putnull(text)); + } + +cleanup: + return (result); +} + +/* + * Act on a "sign" or "loadkeys" command from the command channel. + */ +isc_result_t +named_server_rekey(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + dns_zone_t *zone = NULL; + dns_zonetype_t type; + uint16_t keyopts; + bool fullsign = false; + char *ptr; + + REQUIRE(text != NULL); + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcasecmp(ptr, NAMED_COMMAND_SIGN) == 0) { + fullsign = true; + } + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &zone, NULL, text, false); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (zone == NULL) { + return (ISC_R_UNEXPECTEDEND); /* XXX: or do all zones? */ + } + + type = dns_zone_gettype(zone); + if (type != dns_zone_primary) { + dns_zone_detach(&zone); + return (DNS_R_NOTPRIMARY); + } + + keyopts = dns_zone_getkeyopts(zone); + + /* + * "rndc loadkeys" requires "auto-dnssec maintain" + * or a "dnssec-policy". + */ + if ((keyopts & DNS_ZONEKEY_ALLOW) == 0) { + result = ISC_R_NOPERM; + } else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign) { + result = ISC_R_NOPERM; + } else { + dns_zone_rekey(zone, fullsign); + } + + dns_zone_detach(&zone); + return (result); +} + +/* + * Act on a "sync" command from the command channel. + */ +static isc_result_t +synczone(dns_zone_t *zone, void *uap) { + bool cleanup = *(bool *)uap; + isc_result_t result; + dns_zone_t *raw = NULL; + char *journal; + + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + synczone(raw, uap); + dns_zone_detach(&raw); + } + + result = dns_zone_flush(zone); + if (result != ISC_R_SUCCESS) { + cleanup = false; + } + if (cleanup) { + journal = dns_zone_getjournal(zone); + if (journal != NULL) { + (void)isc_file_remove(journal); + } + } + + return (result); +} + +isc_result_t +named_server_sync(named_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { + isc_result_t result, tresult; + dns_view_t *view; + dns_zone_t *zone = NULL; + char classstr[DNS_RDATACLASS_FORMATSIZE]; + char zonename[DNS_NAME_FORMATSIZE]; + const char *vname, *sep, *arg; + bool cleanup = false; + + REQUIRE(text != NULL); + + (void)next_token(lex, text); + + arg = next_token(lex, text); + if (arg != NULL && + (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) + { + cleanup = true; + arg = next_token(lex, text); + } + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, arg, &zone, NULL, text, false); + if (result != ISC_R_SUCCESS) { + return (result); + } + + if (zone == NULL) { + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + tresult = ISC_R_SUCCESS; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + result = dns_zt_apply(view->zonetable, + isc_rwlocktype_none, false, NULL, + synczone, &cleanup); + if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS) + { + tresult = result; + } + } + isc_task_endexclusive(server->task); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "dumping all zones%s: %s", + cleanup ? ", removing journal files" : "", + isc_result_totext(result)); + return (tresult); + } + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + result = synczone(zone, &cleanup); + isc_task_endexclusive(server->task); + + view = dns_zone_getview(zone); + if (strcmp(view->name, "_default") == 0 || + strcmp(view->name, "_bind") == 0) + { + vname = ""; + sep = ""; + } else { + vname = view->name; + sep = " "; + } + dns_rdataclass_format(dns_zone_getclass(zone), classstr, + sizeof(classstr)); + dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, + ISC_LOG_INFO, "sync: dumping zone '%s/%s'%s%s%s: %s", zonename, + classstr, sep, vname, cleanup ? ", removing journal file" : "", + isc_result_totext(result)); + dns_zone_detach(&zone); + return (result); +} + +/* + * Act on a "freeze" or "thaw" command from the command channel. + */ +isc_result_t +named_server_freeze(named_server_t *server, bool freeze, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result, tresult; + dns_zone_t *mayberaw = NULL, *raw = NULL; + dns_zonetype_t type; + char classstr[DNS_RDATACLASS_FORMATSIZE]; + char zonename[DNS_NAME_FORMATSIZE]; + dns_view_t *view; + const char *vname, *sep; + bool frozen; + const char *msg = NULL; + + REQUIRE(text != NULL); + + result = zone_from_args(server, lex, NULL, &mayberaw, NULL, text, true); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (mayberaw == NULL) { + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + tresult = ISC_R_SUCCESS; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + result = dns_view_freezezones(view, freeze); + if (result != ISC_R_SUCCESS && tresult == ISC_R_SUCCESS) + { + tresult = result; + } + } + isc_task_endexclusive(server->task); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "%s all zones: %s", + freeze ? "freezing" : "thawing", + isc_result_totext(tresult)); + return (tresult); + } + dns_zone_getraw(mayberaw, &raw); + if (raw != NULL) { + dns_zone_detach(&mayberaw); + dns_zone_attach(raw, &mayberaw); + dns_zone_detach(&raw); + } + type = dns_zone_gettype(mayberaw); + if (type != dns_zone_primary) { + dns_zone_detach(&mayberaw); + return (DNS_R_NOTPRIMARY); + } + + if (freeze && !dns_zone_isdynamic(mayberaw, true)) { + dns_zone_detach(&mayberaw); + return (DNS_R_NOTDYNAMIC); + } + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + frozen = dns_zone_getupdatedisabled(mayberaw); + if (freeze) { + if (frozen) { + msg = "WARNING: The zone was already frozen.\n" + "Someone else may be editing it or " + "it may still be re-loading."; + result = DNS_R_FROZEN; + } + if (result == ISC_R_SUCCESS) { + result = dns_zone_flush(mayberaw); + if (result != ISC_R_SUCCESS) { + msg = "Flushing the zone updates to " + "disk failed."; + } + } + if (result == ISC_R_SUCCESS) { + dns_zone_setupdatedisabled(mayberaw, freeze); + } + } else { + if (frozen) { + result = dns_zone_loadandthaw(mayberaw); + switch (result) { + case ISC_R_SUCCESS: + case DNS_R_UPTODATE: + msg = "The zone reload and thaw was " + "successful."; + result = ISC_R_SUCCESS; + break; + case DNS_R_CONTINUE: + msg = "A zone reload and thaw was started.\n" + "Check the logs to see the result."; + result = ISC_R_SUCCESS; + break; + default: + break; + } + } + } + isc_task_endexclusive(server->task); + + if (msg != NULL) { + (void)putstr(text, msg); + (void)putnull(text); + } + + view = dns_zone_getview(mayberaw); + if (strcmp(view->name, "_default") == 0 || + strcmp(view->name, "_bind") == 0) + { + vname = ""; + sep = ""; + } else { + vname = view->name; + sep = " "; + } + dns_rdataclass_format(dns_zone_getclass(mayberaw), classstr, + sizeof(classstr)); + dns_name_format(dns_zone_getorigin(mayberaw), zonename, + sizeof(zonename)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "%s zone '%s/%s'%s%s: %s", + freeze ? "freezing" : "thawing", zonename, classstr, sep, + vname, isc_result_totext(result)); + dns_zone_detach(&mayberaw); + return (result); +} + +#ifdef HAVE_LIBSCF +/* + * This function adds a message for rndc to echo if named + * is managed by smf and is also running chroot. + */ +isc_result_t +named_smf_add_message(isc_buffer_t **text) { + REQUIRE(text != NULL); + + return (putstr(text, "use svcadm(1M) to manage named")); +} +#endif /* HAVE_LIBSCF */ + +#ifndef HAVE_LMDB + +/* + * Emit a comment at the top of the nzf file containing the viewname + * Expects the fp to already be open for writing + */ +#define HEADER1 "# New zone file for view: " +#define HEADER2 \ + "\n# This file contains configuration for zones added by\n" \ + "# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n" +static isc_result_t +add_comment(FILE *fp, const char *viewname) { + isc_result_t result; + CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL)); + CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL)); + CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL)); +cleanup: + return (result); +} + +static void +dumpzone(void *arg, const char *buf, int len) { + FILE *fp = arg; + + (void)isc_stdio_write(buf, len, 1, fp, NULL); +} + +static isc_result_t +nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) { + isc_result_t result; + off_t offset; + FILE *fp = NULL; + bool offsetok = false; + + LOCK(&view->new_zone_lock); + + CHECK(isc_stdio_open(view->new_zone_file, "a", &fp)); + CHECK(isc_stdio_seek(fp, 0, SEEK_END)); + + CHECK(isc_stdio_tell(fp, &offset)); + offsetok = true; + if (offset == 0) { + CHECK(add_comment(fp, view->name)); + } + + CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL)); + cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp); + CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL)); + CHECK(isc_stdio_flush(fp)); + result = isc_stdio_close(fp); + fp = NULL; + +cleanup: + if (fp != NULL) { + (void)isc_stdio_close(fp); + if (offsetok) { + isc_result_t result2; + + result2 = isc_file_truncate(view->new_zone_file, + offset); + if (result2 != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error truncating NZF file '%s' " + "during rollback from append: " + "%s", + view->new_zone_file, + isc_result_totext(result2)); + } + } + } + UNLOCK(&view->new_zone_lock); + return (result); +} + +static isc_result_t +nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) { + const cfg_obj_t *zl = NULL; + cfg_list_t *list; + const cfg_listelt_t *elt; + + FILE *fp = NULL; + char tmp[1024]; + isc_result_t result; + + result = isc_file_template(view->new_zone_file, "nzf-XXXXXXXX", tmp, + sizeof(tmp)); + if (result == ISC_R_SUCCESS) { + result = isc_file_openunique(tmp, &fp); + } + if (result != ISC_R_SUCCESS) { + return (result); + } + + cfg_map_get(config, "zone", &zl); + if (!cfg_obj_islist(zl)) { + CHECK(ISC_R_FAILURE); + } + + DE_CONST(&zl->value.list, list); + + CHECK(add_comment(fp, view->name)); /* force a comment */ + + for (elt = ISC_LIST_HEAD(*list); elt != NULL; + elt = ISC_LIST_NEXT(elt, link)) + { + const cfg_obj_t *zconfig = cfg_listelt_value(elt); + + CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL)); + cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp); + CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL)); + } + + CHECK(isc_stdio_flush(fp)); + result = isc_stdio_close(fp); + fp = NULL; + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + CHECK(isc_file_rename(tmp, view->new_zone_file)); + return (result); + +cleanup: + if (fp != NULL) { + (void)isc_stdio_close(fp); + } + (void)isc_file_remove(tmp); + return (result); +} + +#else /* HAVE_LMDB */ + +static void +nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) { + dns_fixedname_t fixed; + + dns_fixedname_init(&fixed); + dns_name_downcase(name, dns_fixedname_name(&fixed), NULL); + dns_name_format(dns_fixedname_name(&fixed), namebuf, buflen); + + key->mv_data = namebuf; + key->mv_size = strlen(namebuf); +} + +static void +dumpzone(void *arg, const char *buf, int len) { + ns_dzarg_t *dzarg = arg; + isc_result_t result; + + REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC)); + + result = putmem(dzarg->text, buf, len); + if (result != ISC_R_SUCCESS && dzarg->result == ISC_R_SUCCESS) { + dzarg->result = result; + } +} + +static isc_result_t +nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone, + const cfg_obj_t *zconfig) { + isc_result_t result; + int status; + dns_view_t *view; + bool commit = false; + isc_buffer_t *text = NULL; + char namebuf[1024]; + MDB_val key, data; + ns_dzarg_t dzarg; + + view = dns_zone_getview(zone); + + nzd_setkey(&key, dns_zone_getorigin(zone), namebuf, sizeof(namebuf)); + + if (zconfig == NULL) { + /* We're deleting the zone from the database */ + status = mdb_del(*txnp, dbi, &key, NULL); + if (status != MDB_SUCCESS && status != MDB_NOTFOUND) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error deleting zone %s " + "from NZD database: %s", + namebuf, mdb_strerror(status)); + result = ISC_R_FAILURE; + goto cleanup; + } else if (status != MDB_NOTFOUND) { + commit = true; + } + } else { + /* We're creating or overwriting the zone */ + const cfg_obj_t *zoptions; + + isc_buffer_allocate(view->mctx, &text, 256); + + zoptions = cfg_tuple_get(zconfig, "options"); + if (zoptions == NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Unable to get options from config in " + "nzd_save()"); + result = ISC_R_FAILURE; + goto cleanup; + } + + dzarg.magic = DZARG_MAGIC; + dzarg.text = &text; + dzarg.result = ISC_R_SUCCESS; + cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); + if (dzarg.result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error writing zone config to " + "buffer in nzd_save(): %s", + isc_result_totext(dzarg.result)); + result = dzarg.result; + goto cleanup; + } + + data.mv_data = isc_buffer_base(text); + data.mv_size = isc_buffer_usedlength(text); + + status = mdb_put(*txnp, dbi, &key, &data, 0); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error inserting zone in " + "NZD database: %s", + mdb_strerror(status)); + result = ISC_R_FAILURE; + goto cleanup; + } + + commit = true; + } + + result = ISC_R_SUCCESS; + +cleanup: + if (!commit || result != ISC_R_SUCCESS) { + (void)mdb_txn_abort(*txnp); + } else { + status = mdb_txn_commit(*txnp); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error committing " + "NZD database: %s", + mdb_strerror(status)); + result = ISC_R_FAILURE; + } + } + *txnp = NULL; + + if (text != NULL) { + isc_buffer_free(&text); + } + + return (result); +} + +/* + * Check whether the new zone database for 'view' can be opened for writing. + * + * Caller must hold 'view->new_zone_lock'. + */ +static isc_result_t +nzd_writable(dns_view_t *view) { + isc_result_t result = ISC_R_SUCCESS; + int status; + MDB_dbi dbi; + MDB_txn *txn = NULL; + + REQUIRE(view != NULL); + + status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, 0, &txn); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "mdb_txn_begin: %s", mdb_strerror(status)); + return (ISC_R_FAILURE); + } + + status = mdb_dbi_open(txn, NULL, 0, &dbi); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "mdb_dbi_open: %s", mdb_strerror(status)); + result = ISC_R_FAILURE; + } + + mdb_txn_abort(txn); + return (result); +} + +/* + * Open the new zone database for 'view' and start a transaction for it. + * + * Caller must hold 'view->new_zone_lock'. + */ +static isc_result_t +nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) { + int status; + MDB_txn *txn = NULL; + + REQUIRE(view != NULL); + REQUIRE(txnp != NULL && *txnp == NULL); + REQUIRE(dbi != NULL); + + status = mdb_txn_begin((MDB_env *)view->new_zone_dbenv, 0, flags, &txn); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "mdb_txn_begin: %s", mdb_strerror(status)); + goto cleanup; + } + + status = mdb_dbi_open(txn, NULL, 0, dbi); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "mdb_dbi_open: %s", mdb_strerror(status)); + goto cleanup; + } + + *txnp = txn; + +cleanup: + if (status != MDB_SUCCESS) { + if (txn != NULL) { + mdb_txn_abort(txn); + } + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} + +/* + * nzd_env_close() and nzd_env_reopen are a kluge to address the + * problem of an NZD file possibly being created before we drop + * root privileges. + */ +static void +nzd_env_close(dns_view_t *view) { + const char *dbpath = NULL; + char dbpath_copy[PATH_MAX]; + char lockpath[PATH_MAX]; + int status, ret; + + if (view->new_zone_dbenv == NULL) { + return; + } + + status = mdb_env_get_path(view->new_zone_dbenv, &dbpath); + INSIST(status == MDB_SUCCESS); + snprintf(lockpath, sizeof(lockpath), "%s-lock", dbpath); + strlcpy(dbpath_copy, dbpath, sizeof(dbpath_copy)); + mdb_env_close((MDB_env *)view->new_zone_dbenv); + + /* + * Database files must be owned by the eventual user, not by root. + */ + ret = chown(dbpath_copy, ns_os_uid(), -1); + UNUSED(ret); + + /* + * Some platforms need the lockfile not to exist when we reopen the + * environment. + */ + (void)isc_file_remove(lockpath); + + view->new_zone_dbenv = NULL; +} + +static isc_result_t +nzd_env_reopen(dns_view_t *view) { + isc_result_t result; + MDB_env *env = NULL; + int status; + + if (view->new_zone_db == NULL) { + return (ISC_R_SUCCESS); + } + + nzd_env_close(view); + + status = mdb_env_create(&env); + if (status != MDB_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, + "mdb_env_create failed: %s", + mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + + if (view->new_zone_mapsize != 0ULL) { + status = mdb_env_set_mapsize(env, view->new_zone_mapsize); + if (status != MDB_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, + "mdb_env_set_mapsize failed: %s", + mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + } + + status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600); + if (status != MDB_SUCCESS) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_OTHER, ISC_LOG_ERROR, + "mdb_env_open of '%s' failed: %s", + view->new_zone_db, mdb_strerror(status)); + CHECK(ISC_R_FAILURE); + } + + view->new_zone_dbenv = env; + env = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (env != NULL) { + mdb_env_close(env); + } + return (result); +} + +/* + * If 'commit' is true, commit the new zone database transaction pointed to by + * 'txnp'; otherwise, abort that transaction. + * + * Caller must hold 'view->new_zone_lock' for the view that the transaction + * pointed to by 'txnp' was started for. + */ +static isc_result_t +nzd_close(MDB_txn **txnp, bool commit) { + isc_result_t result = ISC_R_SUCCESS; + int status; + + REQUIRE(txnp != NULL); + + if (*txnp != NULL) { + if (commit) { + status = mdb_txn_commit(*txnp); + if (status != MDB_SUCCESS) { + result = ISC_R_FAILURE; + } + } else { + mdb_txn_abort(*txnp); + } + *txnp = NULL; + } + + return (result); +} + +/* + * Count the zones configured in the new zone database for 'view' and store the + * result in 'countp'. + * + * Caller must hold 'view->new_zone_lock'. + */ +static isc_result_t +nzd_count(dns_view_t *view, int *countp) { + isc_result_t result; + int status; + MDB_txn *txn = NULL; + MDB_dbi dbi; + MDB_stat statbuf; + + REQUIRE(countp != NULL); + + result = nzd_open(view, MDB_RDONLY, &txn, &dbi); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + status = mdb_stat(txn, dbi, &statbuf); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "mdb_stat: %s", mdb_strerror(status)); + result = ISC_R_FAILURE; + goto cleanup; + } + + *countp = statbuf.ms_entries; + +cleanup: + (void)nzd_close(&txn, false); + + return (result); +} + +/* + * Migrate zone configuration from an NZF file to an NZD database. + * Caller must hold view->new_zone_lock. + */ +static isc_result_t +migrate_nzf(dns_view_t *view) { + isc_result_t result; + cfg_obj_t *nzf_config = NULL; + int status, n; + isc_buffer_t *text = NULL; + bool commit = false; + const cfg_obj_t *zonelist; + const cfg_listelt_t *element; + char tempname[PATH_MAX]; + MDB_txn *txn = NULL; + MDB_dbi dbi; + MDB_val key, data; + ns_dzarg_t dzarg; + + /* + * If NZF file doesn't exist, or NZD DB exists and already + * has data, return without attempting migration. + */ + if (!isc_file_exists(view->new_zone_file)) { + result = ISC_R_SUCCESS; + goto cleanup; + } + + result = nzd_count(view, &n); + if (result == ISC_R_SUCCESS && n > 0) { + result = ISC_R_SUCCESS; + goto cleanup; + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "Migrating zones from NZF file '%s' to " + "NZD database '%s'", + view->new_zone_file, view->new_zone_db); + /* + * Instead of blindly copying lines, we parse the NZF file using + * the configuration parser, because it validates it against the + * config type, giving us a guarantee that valid configuration + * will be written to DB. + */ + cfg_parser_reset(named_g_addparser); + result = cfg_parse_file(named_g_addparser, view->new_zone_file, + &cfg_type_addzoneconf, &nzf_config); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error parsing NZF file '%s': %s", + view->new_zone_file, isc_result_totext(result)); + goto cleanup; + } + + zonelist = NULL; + CHECK(cfg_map_get(nzf_config, "zone", &zonelist)); + if (!cfg_obj_islist(zonelist)) { + CHECK(ISC_R_FAILURE); + } + + CHECK(nzd_open(view, 0, &txn, &dbi)); + + isc_buffer_allocate(view->mctx, &text, 256); + + for (element = cfg_list_first(zonelist); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *zconfig; + const cfg_obj_t *zoptions; + char zname[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fname; + dns_name_t *name; + const char *origin; + isc_buffer_t b; + + zconfig = cfg_listelt_value(element); + + origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + if (origin == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + /* Normalize zone name */ + isc_buffer_constinit(&b, origin, strlen(origin)); + isc_buffer_add(&b, strlen(origin)); + name = dns_fixedname_initname(&fname); + CHECK(dns_name_fromtext(name, &b, dns_rootname, + DNS_NAME_DOWNCASE, NULL)); + dns_name_format(name, zname, sizeof(zname)); + + key.mv_data = zname; + key.mv_size = strlen(zname); + + zoptions = cfg_tuple_get(zconfig, "options"); + if (zoptions == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + isc_buffer_clear(text); + dzarg.magic = DZARG_MAGIC; + dzarg.text = &text; + dzarg.result = ISC_R_SUCCESS; + cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &dzarg); + if (dzarg.result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error writing zone config to " + "buffer in migrate_nzf(): %s", + isc_result_totext(result)); + result = dzarg.result; + goto cleanup; + } + + data.mv_data = isc_buffer_base(text); + data.mv_size = isc_buffer_usedlength(text); + + status = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE); + if (status != MDB_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Error inserting zone in " + "NZD database: %s", + mdb_strerror(status)); + result = ISC_R_FAILURE; + goto cleanup; + } + + commit = true; + } + + result = ISC_R_SUCCESS; + + /* + * Leaving the NZF file in place is harmless as we won't use it + * if an NZD database is found for the view. But we rename NZF file + * to a backup name here. + */ + strlcpy(tempname, view->new_zone_file, sizeof(tempname)); + if (strlen(tempname) < sizeof(tempname) - 1) { + strlcat(tempname, "~", sizeof(tempname)); + isc_file_rename(view->new_zone_file, tempname); + } + +cleanup: + if (result != ISC_R_SUCCESS) { + (void)nzd_close(&txn, false); + } else { + result = nzd_close(&txn, commit); + } + + if (text != NULL) { + isc_buffer_free(&text); + } + + if (nzf_config != NULL) { + cfg_obj_destroy(named_g_addparser, &nzf_config); + } + + return (result); +} + +#endif /* HAVE_LMDB */ + +static isc_result_t +newzone_parse(named_server_t *server, char *command, dns_view_t **viewp, + cfg_obj_t **zoneconfp, const cfg_obj_t **zoneobjp, + bool *redirectp, isc_buffer_t **text) { + isc_result_t result; + isc_buffer_t argbuf; + bool redirect = false; + cfg_obj_t *zoneconf = NULL; + const cfg_obj_t *zlist = NULL; + const cfg_obj_t *zoneobj = NULL; + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *obj = NULL; + const char *viewname = NULL; + dns_rdataclass_t rdclass; + dns_view_t *view = NULL; + const char *bn = NULL; + + REQUIRE(viewp != NULL && *viewp == NULL); + REQUIRE(zoneobjp != NULL && *zoneobjp == NULL); + REQUIRE(zoneconfp != NULL && *zoneconfp == NULL); + REQUIRE(redirectp != NULL); + + /* Try to parse the argument string */ + isc_buffer_init(&argbuf, command, (unsigned int)strlen(command)); + isc_buffer_add(&argbuf, strlen(command)); + + if (strncasecmp(command, "add", 3) == 0) { + bn = "addzone"; + } else if (strncasecmp(command, "mod", 3) == 0) { + bn = "modzone"; + } else { + UNREACHABLE(); + } + + /* + * Convert the "addzone" or "modzone" to just "zone", for + * the benefit of the parser + */ + isc_buffer_forward(&argbuf, 3); + + cfg_parser_reset(named_g_addparser); + CHECK(cfg_parse_buffer(named_g_addparser, &argbuf, bn, 0, + &cfg_type_addzoneconf, 0, &zoneconf)); + CHECK(cfg_map_get(zoneconf, "zone", &zlist)); + if (!cfg_obj_islist(zlist)) { + CHECK(ISC_R_FAILURE); + } + + /* For now we only support adding one zone at a time */ + zoneobj = cfg_listelt_value(cfg_list_first(zlist)); + + /* Check the zone type for ones that are not supported by addzone. */ + zoptions = cfg_tuple_get(zoneobj, "options"); + + obj = NULL; + (void)cfg_map_get(zoptions, "type", &obj); + if (obj == NULL) { + (void)cfg_map_get(zoptions, "in-view", &obj); + if (obj != NULL) { + (void)putstr(text, "'in-view' zones not supported by "); + (void)putstr(text, bn); + } else { + (void)putstr(text, "zone type not specified"); + } + CHECK(ISC_R_FAILURE); + } + + if (strcasecmp(cfg_obj_asstring(obj), "hint") == 0 || + strcasecmp(cfg_obj_asstring(obj), "forward") == 0 || + strcasecmp(cfg_obj_asstring(obj), "delegation-only") == 0) + { + (void)putstr(text, "'"); + (void)putstr(text, cfg_obj_asstring(obj)); + (void)putstr(text, "' zones not supported by "); + (void)putstr(text, bn); + CHECK(ISC_R_FAILURE); + } + + if (strcasecmp(cfg_obj_asstring(obj), "redirect") == 0) { + redirect = true; + } + + /* Make sense of optional class argument */ + obj = cfg_tuple_get(zoneobj, "class"); + CHECK(named_config_getclass(obj, dns_rdataclass_in, &rdclass)); + + /* Make sense of optional view argument */ + obj = cfg_tuple_get(zoneobj, "view"); + if (obj && cfg_obj_isstring(obj)) { + viewname = cfg_obj_asstring(obj); + } + if (viewname == NULL || *viewname == '\0') { + viewname = "_default"; + } + result = dns_viewlist_find(&server->viewlist, viewname, rdclass, &view); + if (result == ISC_R_NOTFOUND) { + (void)putstr(text, "no matching view found for '"); + (void)putstr(text, viewname); + (void)putstr(text, "'"); + goto cleanup; + } else if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + *viewp = view; + *zoneobjp = zoneobj; + *zoneconfp = zoneconf; + *redirectp = redirect; + + return (ISC_R_SUCCESS); + +cleanup: + if (zoneconf != NULL) { + cfg_obj_destroy(named_g_addparser, &zoneconf); + } + if (view != NULL) { + dns_view_detach(&view); + } + + return (result); +} + +static isc_result_t +delete_zoneconf(dns_view_t *view, cfg_parser_t *pctx, const cfg_obj_t *config, + const dns_name_t *zname, nzfwriter_t nzfwriter) { + isc_result_t result = ISC_R_NOTFOUND; + const cfg_listelt_t *elt = NULL; + const cfg_obj_t *zl = NULL; + cfg_list_t *list; + dns_fixedname_t myfixed; + dns_name_t *myname; + + REQUIRE(view != NULL); + REQUIRE(pctx != NULL); + REQUIRE(config != NULL); + REQUIRE(zname != NULL); + + LOCK(&view->new_zone_lock); + + cfg_map_get(config, "zone", &zl); + + if (!cfg_obj_islist(zl)) { + CHECK(ISC_R_FAILURE); + } + + DE_CONST(&zl->value.list, list); + + myname = dns_fixedname_initname(&myfixed); + + for (elt = ISC_LIST_HEAD(*list); elt != NULL; + elt = ISC_LIST_NEXT(elt, link)) + { + const cfg_obj_t *zconf = cfg_listelt_value(elt); + const char *zn; + cfg_listelt_t *e; + + zn = cfg_obj_asstring(cfg_tuple_get(zconf, "name")); + result = dns_name_fromstring(myname, zn, 0, NULL); + if (result != ISC_R_SUCCESS || !dns_name_equal(zname, myname)) { + continue; + } + + DE_CONST(elt, e); + ISC_LIST_UNLINK(*list, e, link); + cfg_obj_destroy(pctx, &e->obj); + isc_mem_put(pctx->mctx, e, sizeof(*e)); + result = ISC_R_SUCCESS; + break; + } + + /* + * Write config to NZF file if appropriate + */ + if (nzfwriter != NULL && view->new_zone_file != NULL) { + result = nzfwriter(config, view); + } + +cleanup: + UNLOCK(&view->new_zone_lock); + return (result); +} + +static isc_result_t +do_addzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, + dns_name_t *name, cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj, + bool redirect, isc_buffer_t **text) { + isc_result_t result, tresult; + dns_zone_t *zone = NULL; +#ifndef HAVE_LMDB + FILE *fp = NULL; + bool cleanup_config = false; +#else /* HAVE_LMDB */ + MDB_txn *txn = NULL; + MDB_dbi dbi; + bool locked = false; + + UNUSED(zoneconf); +#endif + + /* Zone shouldn't already exist */ + if (redirect) { + result = (view->redirect != NULL) ? ISC_R_SUCCESS + : ISC_R_NOTFOUND; + } else { + result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + } + if (result == ISC_R_SUCCESS) { + result = ISC_R_EXISTS; + goto cleanup; + } else if (result == DNS_R_PARTIALMATCH) { + /* Create our sub-zone anyway */ + dns_zone_detach(&zone); + zone = NULL; + } else if (result != ISC_R_NOTFOUND) { + goto cleanup; + } + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + +#ifndef HAVE_LMDB + /* + * Make sure we can open the configuration save file + */ + result = isc_stdio_open(view->new_zone_file, "a", &fp); + if (result != ISC_R_SUCCESS) { + isc_task_endexclusive(server->task); + TCHECK(putstr(text, "unable to create '")); + TCHECK(putstr(text, view->new_zone_file)); + TCHECK(putstr(text, "': ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + + (void)isc_stdio_close(fp); + fp = NULL; +#else /* HAVE_LMDB */ + LOCK(&view->new_zone_lock); + locked = true; + /* Make sure we can open the NZD database */ + result = nzd_writable(view); + if (result != ISC_R_SUCCESS) { + isc_task_endexclusive(server->task); + TCHECK(putstr(text, "unable to open NZD database for '")); + TCHECK(putstr(text, view->new_zone_db)); + TCHECK(putstr(text, "'")); + result = ISC_R_FAILURE; + goto cleanup; + } +#endif /* HAVE_LMDB */ + + /* Mark view unfrozen and configure zone */ + dns_view_thaw(view); + result = configure_zone(cfg->config, zoneobj, cfg->vconfig, + server->mctx, view, &server->viewlist, + &server->kasplist, cfg->actx, true, false, + false); + dns_view_freeze(view); + + isc_task_endexclusive(server->task); + + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "configure_zone failed: ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + + /* Is it there yet? */ + if (redirect) { + if (view->redirect == NULL) { + CHECK(ISC_R_NOTFOUND); + } + dns_zone_attach(view->redirect, &zone); + } else { + result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "added new zone was not found: %s", + isc_result_totext(result)); + goto cleanup; + } + } + +#ifndef HAVE_LMDB + /* + * If there wasn't a previous newzone config, just save the one + * we've created. If there was a previous one, merge the new + * zone into it. + */ + if (cfg->nzf_config == NULL) { + cfg_obj_attach(zoneconf, &cfg->nzf_config); + } else { + cfg_obj_t *z; + DE_CONST(zoneobj, z); + CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, + "zone")); + } + cleanup_config = true; +#endif /* HAVE_LMDB */ + + /* + * Load the zone from the master file. If this fails, we'll + * need to undo the configuration we've done already. + */ + result = dns_zone_load(zone, true); + if (result != ISC_R_SUCCESS) { + dns_db_t *dbp = NULL; + + TCHECK(putstr(text, "dns_zone_loadnew failed: ")); + TCHECK(putstr(text, isc_result_totext(result))); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "addzone failed; reverting."); + + /* If the zone loaded partially, unload it */ + if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(zone); + } + + /* Remove the zone from the zone table */ + dns_zt_unmount(view->zonetable, zone); + goto cleanup; + } + + /* Flag the zone as having been added at runtime */ + dns_zone_setadded(zone, true); + +#ifdef HAVE_LMDB + /* Save the new zone configuration into the NZD */ + CHECK(nzd_open(view, 0, &txn, &dbi)); + CHECK(nzd_save(&txn, dbi, zone, zoneobj)); +#else /* ifdef HAVE_LMDB */ + /* Append the zone configuration to the NZF */ + result = nzf_append(view, zoneobj); +#endif /* HAVE_LMDB */ + +cleanup: + +#ifndef HAVE_LMDB + if (fp != NULL) { + (void)isc_stdio_close(fp); + } + if (result != ISC_R_SUCCESS && cleanup_config) { + tresult = delete_zoneconf(view, cfg->add_parser, + cfg->nzf_config, name, NULL); + RUNTIME_CHECK(tresult == ISC_R_SUCCESS); + } +#else /* HAVE_LMDB */ + if (txn != NULL) { + (void)nzd_close(&txn, false); + } + if (locked) { + UNLOCK(&view->new_zone_lock); + } +#endif /* HAVE_LMDB */ + + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +static isc_result_t +do_modzone(named_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view, + dns_name_t *name, const char *zname, const cfg_obj_t *zoneobj, + bool redirect, isc_buffer_t **text) { + isc_result_t result, tresult; + dns_zone_t *zone = NULL; + bool added; + bool exclusive = false; +#ifndef HAVE_LMDB + FILE *fp = NULL; + cfg_obj_t *z; +#else /* HAVE_LMDB */ + MDB_txn *txn = NULL; + MDB_dbi dbi; + bool locked = false; +#endif /* HAVE_LMDB */ + + /* Zone must already exist */ + if (redirect) { + if (view->redirect != NULL) { + dns_zone_attach(view->redirect, &zone); + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOTFOUND; + } + } else { + result = dns_zt_find(view->zonetable, name, 0, NULL, &zone); + } + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + added = dns_zone_getadded(zone); + dns_zone_detach(&zone); + +#ifndef HAVE_LMDB + cfg = (ns_cfgctx_t *)view->new_zone_config; + if (cfg == NULL) { + TCHECK(putstr(text, "new zone config is not set")); + CHECK(ISC_R_FAILURE); + } +#endif /* ifndef HAVE_LMDB */ + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + exclusive = true; + +#ifndef HAVE_LMDB + /* Make sure we can open the configuration save file */ + result = isc_stdio_open(view->new_zone_file, "a", &fp); + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "unable to open '")); + TCHECK(putstr(text, view->new_zone_file)); + TCHECK(putstr(text, "': ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + (void)isc_stdio_close(fp); + fp = NULL; +#else /* HAVE_LMDB */ + LOCK(&view->new_zone_lock); + locked = true; + /* Make sure we can open the NZD database */ + result = nzd_writable(view); + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "unable to open NZD database for '")); + TCHECK(putstr(text, view->new_zone_db)); + TCHECK(putstr(text, "'")); + result = ISC_R_FAILURE; + goto cleanup; + } +#endif /* HAVE_LMDB */ + + /* Reconfigure the zone */ + dns_view_thaw(view); + result = configure_zone(cfg->config, zoneobj, cfg->vconfig, + server->mctx, view, &server->viewlist, + &server->kasplist, cfg->actx, true, false, + true); + dns_view_freeze(view); + + exclusive = false; + isc_task_endexclusive(server->task); + + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "configure_zone failed: ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + + /* Is it there yet? */ + if (redirect) { + if (view->redirect == NULL) { + CHECK(ISC_R_NOTFOUND); + } + dns_zone_attach(view->redirect, &zone); + } else { + CHECK(dns_zt_find(view->zonetable, name, 0, NULL, &zone)); + } + +#ifndef HAVE_LMDB + /* Remove old zone from configuration (and NZF file if applicable) */ + if (added) { + result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config, + dns_zone_getorigin(zone), + nzf_writeconf); + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "former zone configuration " + "not deleted: ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + } +#endif /* HAVE_LMDB */ + + if (!added) { + if (cfg->vconfig == NULL) { + result = delete_zoneconf( + view, cfg->conf_parser, cfg->config, + dns_zone_getorigin(zone), NULL); + } else { + const cfg_obj_t *voptions = cfg_tuple_get(cfg->vconfig, + "options"); + result = delete_zoneconf( + view, cfg->conf_parser, voptions, + dns_zone_getorigin(zone), NULL); + } + + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "former zone configuration " + "not deleted: ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } + } + + /* Load the zone from the master file if it needs reloading. */ + result = dns_zone_load(zone, true); + + /* + * Dynamic zones need no reloading, so we can pass this result. + */ + if (result == DNS_R_DYNAMIC) { + result = ISC_R_SUCCESS; + } + + if (result != ISC_R_SUCCESS) { + dns_db_t *dbp = NULL; + + TCHECK(putstr(text, "failed to load zone '")); + TCHECK(putstr(text, zname)); + TCHECK(putstr(text, "': ")); + TCHECK(putstr(text, isc_result_totext(result))); + TCHECK(putstr(text, "\nThe zone is no longer being served. ")); + TCHECK(putstr(text, "Use 'rndc addzone' to correct\n")); + TCHECK(putstr(text, "the problem and restore service.")); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "modzone failed; removing zone."); + + /* If the zone loaded partially, unload it */ + if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(zone); + } + + /* Remove the zone from the zone table */ + dns_zt_unmount(view->zonetable, zone); + goto cleanup; + } + +#ifndef HAVE_LMDB + /* Store the new zone configuration; also in NZF if applicable */ + DE_CONST(zoneobj, z); + CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, "zone")); +#endif /* HAVE_LMDB */ + + if (added) { +#ifdef HAVE_LMDB + CHECK(nzd_open(view, 0, &txn, &dbi)); + CHECK(nzd_save(&txn, dbi, zone, zoneobj)); +#else /* ifdef HAVE_LMDB */ + result = nzf_append(view, zoneobj); + if (result != ISC_R_SUCCESS) { + TCHECK(putstr(text, "\nNew zone config not saved: ")); + TCHECK(putstr(text, isc_result_totext(result))); + goto cleanup; + } +#endif /* HAVE_LMDB */ + + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zname)); + TCHECK(putstr(text, "' reconfigured.")); + } else { + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zname)); + TCHECK(putstr(text, "' must also be reconfigured in\n")); + TCHECK(putstr(text, "named.conf to make changes permanent.")); + } + +cleanup: + if (exclusive) { + isc_task_endexclusive(server->task); + } + +#ifndef HAVE_LMDB + if (fp != NULL) { + (void)isc_stdio_close(fp); + } +#else /* HAVE_LMDB */ + if (txn != NULL) { + (void)nzd_close(&txn, false); + } + if (locked) { + UNLOCK(&view->new_zone_lock); + } +#endif /* HAVE_LMDB */ + + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +/* + * Act on an "addzone" or "modzone" command from the command channel. + */ +isc_result_t +named_server_changezone(named_server_t *server, char *command, + isc_buffer_t **text) { + isc_result_t result; + bool addzone; + bool redirect = false; + ns_cfgctx_t *cfg = NULL; + cfg_obj_t *zoneconf = NULL; + const cfg_obj_t *zoneobj = NULL; + const char *zonename; + dns_view_t *view = NULL; + isc_buffer_t buf; + dns_fixedname_t fname; + dns_name_t *dnsname; + + REQUIRE(text != NULL); + + if (strncasecmp(command, "add", 3) == 0) { + addzone = true; + } else { + INSIST(strncasecmp(command, "mod", 3) == 0); + addzone = false; + } + + CHECK(newzone_parse(server, command, &view, &zoneconf, &zoneobj, + &redirect, text)); + + /* Are we accepting new zones in this view? */ +#ifdef HAVE_LMDB + if (view->new_zone_db == NULL) +#else /* ifdef HAVE_LMDB */ + if (view->new_zone_file == NULL) +#endif /* HAVE_LMDB */ + { + (void)putstr(text, "Not allowing new zones in view '"); + (void)putstr(text, view->name); + (void)putstr(text, "'"); + result = ISC_R_NOPERM; + goto cleanup; + } + + cfg = (ns_cfgctx_t *)view->new_zone_config; + if (cfg == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + zonename = cfg_obj_asstring(cfg_tuple_get(zoneobj, "name")); + isc_buffer_constinit(&buf, zonename, strlen(zonename)); + isc_buffer_add(&buf, strlen(zonename)); + + dnsname = dns_fixedname_initname(&fname); + CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, 0, NULL)); + + if (redirect) { + if (!dns_name_equal(dnsname, dns_rootname)) { + (void)putstr(text, "redirect zones must be called " + "\".\""); + CHECK(ISC_R_FAILURE); + } + } + + if (addzone) { + CHECK(do_addzone(server, cfg, view, dnsname, zoneconf, zoneobj, + redirect, text)); + } else { + CHECK(do_modzone(server, cfg, view, dnsname, zonename, zoneobj, + redirect, text)); + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "%s zone %s in view %s via %s", + addzone ? "added" : "updated", zonename, view->name, + addzone ? NAMED_COMMAND_ADDZONE : NAMED_COMMAND_MODZONE); + + /* Changing a zone counts as reconfiguration */ + CHECK(isc_time_now(&named_g_configtime)); + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + if (zoneconf != NULL) { + cfg_obj_destroy(named_g_addparser, &zoneconf); + } + if (view != NULL) { + dns_view_detach(&view); + } + + return (result); +} + +static bool +inuse(const char *file, bool first, isc_buffer_t **text) { + if (file != NULL && isc_file_exists(file)) { + if (first) { + (void)putstr(text, "The following files were in use " + "and may now be removed:\n"); + } else { + (void)putstr(text, "\n"); + } + (void)putstr(text, file); + (void)putnull(text); + return (false); + } + return (first); +} + +typedef struct { + dns_zone_t *zone; + bool cleanup; +} ns_dzctx_t; + +/* + * Carry out a zone deletion scheduled by named_server_delzone(). + */ +static void +rmzone(isc_task_t *task, isc_event_t *event) { + ns_dzctx_t *dz = (ns_dzctx_t *)event->ev_arg; + dns_zone_t *zone = NULL, *raw = NULL, *mayberaw = NULL; + dns_catz_zone_t *catz = NULL; + char zonename[DNS_NAME_FORMATSIZE]; + dns_view_t *view = NULL; + ns_cfgctx_t *cfg = NULL; + dns_db_t *dbp = NULL; + bool added; + isc_result_t result; +#ifdef HAVE_LMDB + MDB_txn *txn = NULL; + MDB_dbi dbi; +#endif /* ifdef HAVE_LMDB */ + + REQUIRE(dz != NULL); + + isc_event_free(&event); + + /* Dig out configuration for this zone */ + zone = dz->zone; + view = dns_zone_getview(zone); + cfg = (ns_cfgctx_t *)view->new_zone_config; + dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename)); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "deleting zone %s in view %s via delzone", zonename, + view->name); + + /* + * Remove the zone from configuration (and NZF file if applicable) + * (If this is a catalog zone member then nzf_config can be NULL) + */ + added = dns_zone_getadded(zone); + catz = dns_zone_get_parentcatz(zone); + + if (added && catz == NULL && cfg != NULL) { +#ifdef HAVE_LMDB + /* Make sure we can open the NZD database */ + LOCK(&view->new_zone_lock); + result = nzd_open(view, 0, &txn, &dbi); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "unable to open NZD database for '%s'", + view->new_zone_db); + } else { + result = nzd_save(&txn, dbi, zone, NULL); + } + + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "unable to delete zone configuration: %s", + isc_result_totext(result)); + } + + if (txn != NULL) { + (void)nzd_close(&txn, false); + } + UNLOCK(&view->new_zone_lock); +#else /* ifdef HAVE_LMDB */ + result = delete_zoneconf(view, cfg->add_parser, cfg->nzf_config, + dns_zone_getorigin(zone), + nzf_writeconf); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "unable to delete zone configuration: %s", + isc_result_totext(result)); + } +#endif /* HAVE_LMDB */ + } + + if (!added && cfg != NULL) { + if (cfg->vconfig != NULL) { + const cfg_obj_t *voptions = cfg_tuple_get(cfg->vconfig, + "options"); + result = delete_zoneconf( + view, cfg->conf_parser, voptions, + dns_zone_getorigin(zone), NULL); + } else { + result = delete_zoneconf( + view, cfg->conf_parser, cfg->config, + dns_zone_getorigin(zone), NULL); + } + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "unable to delete zone configuration: %s", + isc_result_totext(result)); + } + } + + /* Unload zone database */ + if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(zone); + } + + /* Clean up stub/secondary zone files if requested to do so */ + dns_zone_getraw(zone, &raw); + mayberaw = (raw != NULL) ? raw : zone; + + if (added && dz->cleanup) { + const char *file; + + file = dns_zone_getfile(mayberaw); + result = isc_file_remove(file); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + + file = dns_zone_getjournal(mayberaw); + result = isc_file_remove(file); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + + if (zone != mayberaw) { + file = dns_zone_getfile(zone); + result = isc_file_remove(file); + if (result != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + + file = dns_zone_getjournal(zone); + result = isc_file_remove(file); + if (result != ISC_R_SUCCESS) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + } + } + + if (raw != NULL) { + dns_zone_detach(&raw); + } + dns_zone_detach(&zone); + isc_mem_put(named_g_mctx, dz, sizeof(*dz)); + isc_task_detach(&task); +} + +/* + * Act on a "delzone" command from the command channel. + */ +isc_result_t +named_server_delzone(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result, tresult; + dns_zone_t *zone = NULL; + dns_zone_t *raw = NULL; + dns_zone_t *mayberaw; + dns_view_t *view = NULL; + char zonename[DNS_NAME_FORMATSIZE]; + bool cleanup = false; + const char *ptr; + bool added; + ns_dzctx_t *dz = NULL; + isc_event_t *dzevent = NULL; + isc_task_t *task = NULL; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Find out what we are to do. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcmp(ptr, "-clean") == 0 || strcmp(ptr, "-clear") == 0) { + cleanup = true; + ptr = next_token(lex, text); + } + + CHECK(zone_from_args(server, lex, ptr, &zone, zonename, text, false)); + if (zone == NULL) { + result = ISC_R_UNEXPECTEDEND; + goto cleanup; + } + + INSIST(zonename != NULL); + + /* Is this a policy zone? */ + if (dns_zone_get_rpz_num(zone) != DNS_RPZ_INVALID_NUM) { + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zonename)); + TCHECK(putstr(text, + "' cannot be deleted: response-policy zone.")); + result = ISC_R_FAILURE; + goto cleanup; + } + + view = dns_zone_getview(zone); + if (dns_zone_gettype(zone) == dns_zone_redirect) { + dns_zone_detach(&view->redirect); + } else { + CHECK(dns_zt_unmount(view->zonetable, zone)); + } + + /* Send cleanup event */ + dz = isc_mem_get(named_g_mctx, sizeof(*dz)); + + dz->cleanup = cleanup; + dz->zone = NULL; + dns_zone_attach(zone, &dz->zone); + dzevent = isc_event_allocate(named_g_mctx, server, NAMED_EVENT_DELZONE, + rmzone, dz, sizeof(isc_event_t)); + + dns_zone_gettask(zone, &task); + isc_task_send(task, &dzevent); + dz = NULL; + + /* Inform user about cleaning up stub/secondary zone files */ + dns_zone_getraw(zone, &raw); + mayberaw = (raw != NULL) ? raw : zone; + + added = dns_zone_getadded(zone); + if (!added) { + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zonename)); + TCHECK(putstr(text, "' is no longer active and will be " + "deleted.\n")); + TCHECK(putstr(text, "To keep it from returning ")); + TCHECK(putstr(text, "when the server is restarted, it\n")); + TCHECK(putstr(text, "must also be removed from named.conf.")); + } else if (cleanup) { + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zonename)); + TCHECK(putstr(text, "' and associated files will be deleted.")); + } else if (dns_zone_gettype(mayberaw) == dns_zone_secondary || + dns_zone_gettype(mayberaw) == dns_zone_mirror || + dns_zone_gettype(mayberaw) == dns_zone_stub) + { + bool first; + const char *file; + + TCHECK(putstr(text, "zone '")); + TCHECK(putstr(text, zonename)); + TCHECK(putstr(text, "' will be deleted.")); + + file = dns_zone_getfile(mayberaw); + first = inuse(file, true, text); + + file = dns_zone_getjournal(mayberaw); + first = inuse(file, first, text); + + if (zone != mayberaw) { + file = dns_zone_getfile(zone); + first = inuse(file, first, text); + + file = dns_zone_getjournal(zone); + (void)inuse(file, first, text); + } + } + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "zone %s scheduled for removal via delzone", zonename); + + /* Removing a zone counts as reconfiguration */ + CHECK(isc_time_now(&named_g_configtime)); + + result = ISC_R_SUCCESS; + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + if (raw != NULL) { + dns_zone_detach(&raw); + } + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +static const cfg_obj_t * +find_name_in_list_from_map(const cfg_obj_t *config, + const char *map_key_for_list, const char *name, + bool redirect) { + const cfg_obj_t *list = NULL; + const cfg_listelt_t *element; + const cfg_obj_t *obj = NULL; + dns_fixedname_t fixed1, fixed2; + dns_name_t *name1 = NULL, *name2 = NULL; + isc_result_t result; + + if (strcmp(map_key_for_list, "zone") == 0) { + name1 = dns_fixedname_initname(&fixed1); + name2 = dns_fixedname_initname(&fixed2); + result = dns_name_fromstring(name1, name, 0, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + + cfg_map_get(config, map_key_for_list, &list); + for (element = cfg_list_first(list); element != NULL; + element = cfg_list_next(element)) + { + const char *vname; + + obj = cfg_listelt_value(element); + INSIST(obj != NULL); + vname = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + if (vname == NULL) { + obj = NULL; + continue; + } + + if (name1 != NULL) { + result = dns_name_fromstring(name2, vname, 0, NULL); + if (result == ISC_R_SUCCESS && + dns_name_equal(name1, name2)) + { + const cfg_obj_t *zoptions; + const cfg_obj_t *typeobj = NULL; + zoptions = cfg_tuple_get(obj, "options"); + + if (zoptions != NULL) { + cfg_map_get(zoptions, "type", &typeobj); + } + if (redirect && typeobj != NULL && + strcasecmp(cfg_obj_asstring(typeobj), + "redirect") == 0) + { + break; + } else if (!redirect) { + break; + } + } + } else if (strcasecmp(vname, name) == 0) { + break; + } + + obj = NULL; + } + + return (obj); +} + +static void +emitzone(void *arg, const char *buf, int len) { + ns_dzarg_t *dzarg = arg; + isc_result_t result; + + REQUIRE(dzarg != NULL && ISC_MAGIC_VALID(dzarg, DZARG_MAGIC)); + result = putmem(dzarg->text, buf, len); + if (result != ISC_R_SUCCESS && dzarg->result == ISC_R_SUCCESS) { + dzarg->result = result; + } +} + +/* + * Act on a "showzone" command from the command channel. + */ +isc_result_t +named_server_showzone(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result; + const cfg_obj_t *vconfig = NULL, *zconfig = NULL; + char zonename[DNS_NAME_FORMATSIZE]; + const cfg_obj_t *map; + dns_view_t *view = NULL; + dns_zone_t *zone = NULL; + ns_cfgctx_t *cfg = NULL; +#ifdef HAVE_LMDB + cfg_obj_t *nzconfig = NULL; +#endif /* HAVE_LMDB */ + bool added, redirect; + ns_dzarg_t dzarg; + + REQUIRE(text != NULL); + + /* Parse parameters */ + CHECK(zone_from_args(server, lex, NULL, &zone, zonename, text, true)); + if (zone == NULL) { + result = ISC_R_UNEXPECTEDEND; + goto cleanup; + } + + redirect = dns_zone_gettype(zone) == dns_zone_redirect; + added = dns_zone_getadded(zone); + view = dns_zone_getview(zone); + dns_zone_detach(&zone); + + cfg = (ns_cfgctx_t *)view->new_zone_config; + if (cfg == NULL) { + result = ISC_R_FAILURE; + goto cleanup; + } + + if (!added) { + /* Find the view statement */ + vconfig = find_name_in_list_from_map(cfg->config, "view", + view->name, false); + + /* Find the zone statement */ + if (vconfig != NULL) { + map = cfg_tuple_get(vconfig, "options"); + } else { + map = cfg->config; + } + + zconfig = find_name_in_list_from_map(map, "zone", zonename, + redirect); + } + +#ifndef HAVE_LMDB + if (zconfig == NULL && cfg->nzf_config != NULL) { + zconfig = find_name_in_list_from_map(cfg->nzf_config, "zone", + zonename, redirect); + } +#else /* HAVE_LMDB */ + if (zconfig == NULL) { + const cfg_obj_t *zlist = NULL; + CHECK(get_newzone_config(view, zonename, &nzconfig)); + CHECK(cfg_map_get(nzconfig, "zone", &zlist)); + if (!cfg_obj_islist(zlist)) { + CHECK(ISC_R_FAILURE); + } + + zconfig = cfg_listelt_value(cfg_list_first(zlist)); + } +#endif /* HAVE_LMDB */ + + if (zconfig == NULL) { + CHECK(ISC_R_NOTFOUND); + } + + CHECK(putstr(text, "zone ")); + dzarg.magic = DZARG_MAGIC; + dzarg.text = text; + dzarg.result = ISC_R_SUCCESS; + cfg_printx(zconfig, CFG_PRINTER_ONELINE, emitzone, &dzarg); + CHECK(dzarg.result); + + CHECK(putstr(text, ";")); + + result = ISC_R_SUCCESS; + +cleanup: +#ifdef HAVE_LMDB + if (nzconfig != NULL) { + cfg_obj_destroy(named_g_addparser, &nzconfig); + } +#endif /* HAVE_LMDB */ + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} + +static void +newzone_cfgctx_destroy(void **cfgp) { + ns_cfgctx_t *cfg; + + REQUIRE(cfgp != NULL && *cfgp != NULL); + + cfg = *cfgp; + + if (cfg->conf_parser != NULL) { + if (cfg->config != NULL) { + cfg_obj_destroy(cfg->conf_parser, &cfg->config); + } + if (cfg->vconfig != NULL) { + cfg_obj_destroy(cfg->conf_parser, &cfg->vconfig); + } + cfg_parser_destroy(&cfg->conf_parser); + } + if (cfg->add_parser != NULL) { + if (cfg->nzf_config != NULL) { + cfg_obj_destroy(cfg->add_parser, &cfg->nzf_config); + } + cfg_parser_destroy(&cfg->add_parser); + } + + if (cfg->actx != NULL) { + cfg_aclconfctx_detach(&cfg->actx); + } + + isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg)); + *cfgp = NULL; +} + +isc_result_t +named_server_signing(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_zone_t *zone = NULL; + dns_name_t *origin; + dns_db_t *db = NULL; + dns_dbnode_t *node = NULL; + dns_dbversion_t *version = NULL; + dns_rdatatype_t privatetype; + dns_rdataset_t privset; + bool first = true; + bool list = false, clear = false; + bool chain = false; + bool setserial = false; + bool resalt = false; + uint32_t serial = 0; + char keystr[DNS_SECALG_FORMATSIZE + 7]; /* <5-digit keyid>/ */ + unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0; + unsigned char salt[255]; + const char *ptr; + size_t n; + + REQUIRE(text != NULL); + + dns_rdataset_init(&privset); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Find out what we are to do. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcasecmp(ptr, "-list") == 0) { + list = true; + } else if ((strcasecmp(ptr, "-clear") == 0) || + (strcasecmp(ptr, "-clean") == 0)) + { + clear = true; + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + strlcpy(keystr, ptr, sizeof(keystr)); + } else if (strcasecmp(ptr, "-nsec3param") == 0) { + char hashbuf[64], flagbuf[64], iterbuf[64]; + char nbuf[256]; + + chain = true; + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcasecmp(ptr, "none") == 0) { + hash = 0; + } else { + strlcpy(hashbuf, ptr, sizeof(hashbuf)); + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + strlcpy(flagbuf, ptr, sizeof(flagbuf)); + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + strlcpy(iterbuf, ptr, sizeof(iterbuf)); + n = snprintf(nbuf, sizeof(nbuf), "%s %s %s", hashbuf, + flagbuf, iterbuf); + if (n == sizeof(nbuf)) { + return (ISC_R_NOSPACE); + } + n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter); + if (n != 3U) { + return (ISC_R_BADNUMBER); + } + + if (hash > 0xffU || flags > 0xffU || + iter > dns_nsec3_maxiterations()) + { + return (ISC_R_RANGE); + } + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } else if (strcasecmp(ptr, "auto") == 0) { + /* Auto-generate a random salt. + * XXXMUKS: This currently uses the + * minimum recommended length by RFC + * 5155 (64 bits). It should be made + * configurable. + */ + saltlen = 8; + resalt = true; + } else if (strcmp(ptr, "-") != 0) { + isc_buffer_t buf; + + isc_buffer_init(&buf, salt, sizeof(salt)); + CHECK(isc_hex_decodestring(ptr, &buf)); + saltlen = isc_buffer_usedlength(&buf); + } + } + } else if (strcasecmp(ptr, "-serial") == 0) { + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + CHECK(isc_parse_uint32(&serial, ptr, 10)); + setserial = true; + } else { + CHECK(DNS_R_SYNTAX); + } + + CHECK(zone_from_args(server, lex, NULL, &zone, NULL, text, false)); + if (zone == NULL) { + CHECK(ISC_R_UNEXPECTEDEND); + } + + if (dns_zone_getkasp(zone) != NULL) { + (void)putstr(text, "zone uses dnssec-policy, use rndc dnssec " + "command instead"); + (void)putnull(text); + goto cleanup; + } + + if (clear) { + CHECK(dns_zone_keydone(zone, keystr)); + (void)putstr(text, "request queued"); + (void)putnull(text); + } else if (chain) { + CHECK(dns_zone_setnsec3param( + zone, (uint8_t)hash, (uint8_t)flags, iter, + (uint8_t)saltlen, salt, true, resalt)); + (void)putstr(text, "nsec3param request queued"); + (void)putnull(text); + } else if (setserial) { + CHECK(dns_zone_setserial(zone, serial)); + (void)putstr(text, "serial request queued"); + (void)putnull(text); + } else if (list) { + privatetype = dns_zone_getprivatetype(zone); + origin = dns_zone_getorigin(zone); + CHECK(dns_zone_getdb(zone, &db)); + CHECK(dns_db_findnode(db, origin, false, &node)); + dns_db_currentversion(db, &version); + + result = dns_db_findrdataset(db, node, version, privatetype, + dns_rdatatype_none, 0, &privset, + NULL); + if (result == ISC_R_NOTFOUND) { + (void)putstr(text, "No signing records found"); + (void)putnull(text); + result = ISC_R_SUCCESS; + goto cleanup; + } + + for (result = dns_rdataset_first(&privset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&privset)) + { + dns_rdata_t priv = DNS_RDATA_INIT; + /* + * In theory, the output buffer could hold a full RDATA + * record which is 16-bit and then some text around + * it + */ + char output[UINT16_MAX + BUFSIZ]; + isc_buffer_t buf; + + dns_rdataset_current(&privset, &priv); + + isc_buffer_init(&buf, output, sizeof(output)); + CHECK(dns_private_totext(&priv, &buf)); + if (!first) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, output)); + first = false; + } + if (!first) { + CHECK(putnull(text)); + } + + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + } + +cleanup: + if (dns_rdataset_isassociated(&privset)) { + dns_rdataset_disassociate(&privset); + } + if (node != NULL) { + dns_db_detachnode(db, &node); + } + if (version != NULL) { + dns_db_closeversion(db, &version, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +static bool +argcheck(char *cmd, const char *full) { + size_t l; + + if (cmd == NULL || cmd[0] != '-') { + return (false); + } + + cmd++; + l = strlen(cmd); + if (l > strlen(full) || strncasecmp(cmd, full, l) != 0) { + return (false); + } + + return (true); +} + +isc_result_t +named_server_dnssec(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_zone_t *zone = NULL; + dns_kasp_t *kasp = NULL; + dns_dnsseckeylist_t keys; + dns_dnsseckey_t *key; + char *ptr, *zonetext = NULL; + const char *msg = NULL; + /* variables for -checkds */ + bool checkds = false, dspublish = false; + /* variables for -rollover */ + bool rollover = false; + /* variables for -key */ + bool use_keyid = false; + dns_keytag_t keyid = 0; + uint8_t algorithm = 0; + /* variables for -status */ + bool status = false; + char output[4096]; + isc_stdtime_t now, when; + isc_time_t timenow, timewhen; + const char *dir; + dns_db_t *db = NULL; + dns_dbversion_t *version = NULL; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Find out what we are to do. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Initialize current time and key list. */ + TIME_NOW(&timenow); + now = isc_time_seconds(&timenow); + when = now; + + ISC_LIST_INIT(keys); + + if (strcasecmp(ptr, "-status") == 0) { + status = true; + } else if (strcasecmp(ptr, "-rollover") == 0) { + rollover = true; + } else if (strcasecmp(ptr, "-checkds") == 0) { + checkds = true; + } else { + CHECK(DNS_R_SYNTAX); + } + + if (rollover || checkds) { + /* Check for options */ + for (;;) { + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "Bad format"; + CHECK(ISC_R_UNEXPECTEDEND); + } else if (argcheck(ptr, "alg")) { + isc_consttextregion_t alg; + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "No key algorithm specified"; + CHECK(ISC_R_UNEXPECTEDEND); + } + alg.base = ptr; + alg.length = strlen(alg.base); + result = dns_secalg_fromtext( + &algorithm, (isc_textregion_t *)&alg); + if (result != ISC_R_SUCCESS) { + msg = "Bad algorithm"; + CHECK(DNS_R_SYNTAX); + } + continue; + } else if (argcheck(ptr, "key")) { + uint16_t id; + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "No key identifier specified"; + CHECK(ISC_R_UNEXPECTEDEND); + } + CHECK(isc_parse_uint16(&id, ptr, 10)); + keyid = (dns_keytag_t)id; + use_keyid = true; + continue; + } else if (argcheck(ptr, "when")) { + uint32_t tw; + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "No time specified"; + CHECK(ISC_R_UNEXPECTEDEND); + } + CHECK(dns_time32_fromtext(ptr, &tw)); + when = (isc_stdtime_t)tw; + continue; + } else if (ptr[0] == '-') { + msg = "Unknown option"; + CHECK(DNS_R_SYNTAX); + } else if (checkds) { + /* + * No arguments provided, so we must be + * parsing "published|withdrawn". + */ + if (strcasecmp(ptr, "published") == 0) { + dspublish = true; + } else if (strcasecmp(ptr, "withdrawn") != 0) { + CHECK(DNS_R_SYNTAX); + } + } else if (rollover) { + /* + * No arguments provided, so we must be + * parsing the zone. + */ + zonetext = ptr; + } + break; + } + + if (rollover && !use_keyid) { + msg = "Key id is required when scheduling rollover"; + CHECK(DNS_R_SYNTAX); + } + + if (algorithm > 0 && !use_keyid) { + msg = "Key id is required when setting algorithm"; + CHECK(DNS_R_SYNTAX); + } + } + + /* Get zone. */ + CHECK(zone_from_args(server, lex, zonetext, &zone, NULL, text, false)); + if (zone == NULL) { + msg = "Zone not found"; + CHECK(ISC_R_UNEXPECTEDEND); + } + + /* Trailing garbage? */ + ptr = next_token(lex, text); + if (ptr != NULL) { + msg = "Too many arguments"; + CHECK(DNS_R_SYNTAX); + } + + /* Get dnssec-policy. */ + kasp = dns_zone_getkasp(zone); + if (kasp == NULL) { + msg = "Zone does not have dnssec-policy"; + goto cleanup; + } + + /* Get DNSSEC keys. */ + dir = dns_zone_getkeydirectory(zone); + CHECK(dns_zone_getdb(zone, &db)); + dns_db_currentversion(db, &version); + LOCK(&kasp->lock); + result = dns_zone_getdnsseckeys(zone, db, version, now, &keys); + UNLOCK(&kasp->lock); + if (result != ISC_R_SUCCESS) { + if (result != ISC_R_NOTFOUND) { + goto cleanup; + } + } + + if (status) { + /* + * Output the DNSSEC status of the key and signing policy. + */ + LOCK(&kasp->lock); + dns_keymgr_status(kasp, &keys, now, &output[0], sizeof(output)); + UNLOCK(&kasp->lock); + CHECK(putstr(text, output)); + } else if (checkds) { + /* + * Mark DS record has been seen, so it may move to the + * rumoured state. + */ + char whenbuf[80]; + isc_time_set(&timewhen, when, 0); + isc_time_formattimestamp(&timewhen, whenbuf, sizeof(whenbuf)); + isc_result_t ret; + + LOCK(&kasp->lock); + if (use_keyid) { + result = dns_keymgr_checkds_id(kasp, &keys, dir, now, + when, dspublish, keyid, + (unsigned int)algorithm); + } else { + result = dns_keymgr_checkds(kasp, &keys, dir, now, when, + dspublish); + } + UNLOCK(&kasp->lock); + + switch (result) { + case ISC_R_SUCCESS: + /* + * Rekey after checkds command because the next key + * event may have changed. + */ + dns_zone_rekey(zone, false); + + if (use_keyid) { + char tagbuf[6]; + snprintf(tagbuf, sizeof(tagbuf), "%u", keyid); + CHECK(putstr(text, "KSK ")); + CHECK(putstr(text, tagbuf)); + CHECK(putstr(text, ": ")); + } + CHECK(putstr(text, "Marked DS as ")); + if (dspublish) { + CHECK(putstr(text, "published ")); + } else { + CHECK(putstr(text, "withdrawn ")); + } + CHECK(putstr(text, "since ")); + CHECK(putstr(text, whenbuf)); + break; + case DNS_R_TOOMANYKEYS: + CHECK(putstr(text, + "Error: multiple possible keys found, " + "retry command with -key id")); + break; + default: + ret = result; + CHECK(putstr(text, + "Error executing checkds command: ")); + CHECK(putstr(text, isc_result_totext(ret))); + break; + } + } else if (rollover) { + /* + * Manually rollover a key. + */ + char whenbuf[80]; + isc_time_set(&timewhen, when, 0); + isc_time_formattimestamp(&timewhen, whenbuf, sizeof(whenbuf)); + isc_result_t ret; + + LOCK(&kasp->lock); + result = dns_keymgr_rollover(kasp, &keys, dir, now, when, keyid, + (unsigned int)algorithm); + UNLOCK(&kasp->lock); + + switch (result) { + case ISC_R_SUCCESS: + /* + * Rekey after rollover command because the next key + * event may have changed. + */ + dns_zone_rekey(zone, false); + + if (use_keyid) { + char tagbuf[6]; + snprintf(tagbuf, sizeof(tagbuf), "%u", keyid); + CHECK(putstr(text, "Key ")); + CHECK(putstr(text, tagbuf)); + CHECK(putstr(text, ": ")); + } + CHECK(putstr(text, "Rollover scheduled on ")); + CHECK(putstr(text, whenbuf)); + break; + case DNS_R_TOOMANYKEYS: + CHECK(putstr(text, + "Error: multiple possible keys found, " + "retry command with -alg algorithm")); + break; + default: + ret = result; + CHECK(putstr(text, + "Error executing rollover command: ")); + CHECK(putstr(text, isc_result_totext(ret))); + break; + } + } + CHECK(putnull(text)); + +cleanup: + if (msg != NULL) { + (void)putstr(text, msg); + (void)putnull(text); + } + + if (version != NULL) { + dns_db_closeversion(db, &version, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + + while (!ISC_LIST_EMPTY(keys)) { + key = ISC_LIST_HEAD(keys); + ISC_LIST_UNLINK(keys, key, link); + dns_dnsseckey_destroy(dns_zone_getmctx(zone), &key); + } + + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +static isc_result_t +putmem(isc_buffer_t **b, const char *str, size_t len) { + isc_result_t result; + + result = isc_buffer_reserve(b, (unsigned int)len); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + + isc_buffer_putmem(*b, (const unsigned char *)str, (unsigned int)len); + return (ISC_R_SUCCESS); +} + +static isc_result_t +putstr(isc_buffer_t **b, const char *str) { + return (putmem(b, str, strlen(str))); +} + +static isc_result_t +putuint8(isc_buffer_t **b, uint8_t val) { + isc_result_t result; + + result = isc_buffer_reserve(b, 1); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + + isc_buffer_putuint8(*b, val); + return (ISC_R_SUCCESS); +} + +static isc_result_t +putnull(isc_buffer_t **b) { + return (putuint8(b, 0)); +} + +isc_result_t +named_server_zonestatus(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + isc_result_t result = ISC_R_SUCCESS; + dns_zone_t *zone = NULL, *raw = NULL, *mayberaw = NULL; + const char *type, *file; + char zonename[DNS_NAME_FORMATSIZE]; + uint32_t serial, signed_serial, nodes; + char serbuf[16], sserbuf[16], nodebuf[16]; + char resignbuf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + 2]; + char lbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char xbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char rbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char kbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + char rtbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + isc_time_t loadtime, expiretime, refreshtime; + isc_time_t refreshkeytime, resigntime; + dns_zonetype_t zonetype; + bool dynamic = false, frozen = false; + bool hasraw = false; + bool secure, maintain, allow; + dns_db_t *db = NULL, *rawdb = NULL; + char **incfiles = NULL; + int nfiles = 0; + + REQUIRE(text != NULL); + + isc_time_settoepoch(&loadtime); + isc_time_settoepoch(&refreshtime); + isc_time_settoepoch(&expiretime); + isc_time_settoepoch(&refreshkeytime); + isc_time_settoepoch(&resigntime); + + CHECK(zone_from_args(server, lex, NULL, &zone, zonename, text, true)); + if (zone == NULL) { + result = ISC_R_UNEXPECTEDEND; + goto cleanup; + } + + /* Inline signing? */ + CHECK(dns_zone_getdb(zone, &db)); + dns_zone_getraw(zone, &raw); + hasraw = (raw != NULL); + if (hasraw) { + mayberaw = raw; + zonetype = dns_zone_gettype(raw); + CHECK(dns_zone_getdb(raw, &rawdb)); + } else { + mayberaw = zone; + zonetype = dns_zone_gettype(zone); + } + + type = dns_zonetype_name(zonetype); + + /* Serial number */ + result = dns_zone_getserial(mayberaw, &serial); + + /* This is to mirror old behavior with dns_zone_getserial */ + if (result != ISC_R_SUCCESS) { + serial = 0; + } + + snprintf(serbuf, sizeof(serbuf), "%u", serial); + if (hasraw) { + result = dns_zone_getserial(zone, &signed_serial); + if (result != ISC_R_SUCCESS) { + serial = 0; + } + snprintf(sserbuf, sizeof(sserbuf), "%u", signed_serial); + } + + /* Database node count */ + nodes = dns_db_nodecount(hasraw ? rawdb : db, dns_dbtree_main); + snprintf(nodebuf, sizeof(nodebuf), "%u", nodes); + + /* Security */ + secure = dns_db_issecure(db); + allow = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_ALLOW) != 0); + maintain = ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0); + + /* Master files */ + file = dns_zone_getfile(mayberaw); + nfiles = dns_zone_getincludes(mayberaw, &incfiles); + + /* Load time */ + dns_zone_getloadtime(zone, &loadtime); + isc_time_formathttptimestamp(&loadtime, lbuf, sizeof(lbuf)); + + /* Refresh/expire times */ + if (zonetype == dns_zone_secondary || zonetype == dns_zone_mirror || + zonetype == dns_zone_stub || zonetype == dns_zone_redirect) + { + dns_zone_getexpiretime(mayberaw, &expiretime); + isc_time_formathttptimestamp(&expiretime, xbuf, sizeof(xbuf)); + dns_zone_getrefreshtime(mayberaw, &refreshtime); + isc_time_formathttptimestamp(&refreshtime, rbuf, sizeof(rbuf)); + } + + /* Key refresh time */ + if (zonetype == dns_zone_primary || + (zonetype == dns_zone_secondary && hasraw)) + { + dns_zone_getrefreshkeytime(zone, &refreshkeytime); + isc_time_formathttptimestamp(&refreshkeytime, kbuf, + sizeof(kbuf)); + } + + /* Dynamic? */ + if (zonetype == dns_zone_primary) { + dynamic = dns_zone_isdynamic(mayberaw, true); + frozen = dynamic && !dns_zone_isdynamic(mayberaw, false); + } + + /* Next resign event */ + if (secure && + (zonetype == dns_zone_primary || + (zonetype == dns_zone_secondary && hasraw)) && + ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_NORESIGN) == 0)) + { + dns_name_t *name; + dns_fixedname_t fixed; + dns_rdataset_t next; + + dns_rdataset_init(&next); + name = dns_fixedname_initname(&fixed); + + result = dns_db_getsigningtime(db, &next, name); + if (result == ISC_R_SUCCESS) { + isc_stdtime_t timenow; + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + + isc_stdtime_get(&timenow); + dns_name_format(name, namebuf, sizeof(namebuf)); + dns_rdatatype_format(next.covers, typebuf, + sizeof(typebuf)); + snprintf(resignbuf, sizeof(resignbuf), "%s/%s", namebuf, + typebuf); + isc_time_set( + &resigntime, + next.resign - + dns_zone_getsigresigninginterval(zone), + 0); + isc_time_formathttptimestamp(&resigntime, rtbuf, + sizeof(rtbuf)); + dns_rdataset_disassociate(&next); + } + } + + /* Create text */ + CHECK(putstr(text, "name: ")); + CHECK(putstr(text, zonename)); + + CHECK(putstr(text, "\ntype: ")); + CHECK(putstr(text, type)); + + if (file != NULL) { + int i; + CHECK(putstr(text, "\nfiles: ")); + CHECK(putstr(text, file)); + for (i = 0; i < nfiles; i++) { + CHECK(putstr(text, ", ")); + if (incfiles[i] != NULL) { + CHECK(putstr(text, incfiles[i])); + } + } + } + + CHECK(putstr(text, "\nserial: ")); + CHECK(putstr(text, serbuf)); + if (hasraw) { + CHECK(putstr(text, "\nsigned serial: ")); + CHECK(putstr(text, sserbuf)); + } + + CHECK(putstr(text, "\nnodes: ")); + CHECK(putstr(text, nodebuf)); + + if (!isc_time_isepoch(&loadtime)) { + CHECK(putstr(text, "\nlast loaded: ")); + CHECK(putstr(text, lbuf)); + } + + if (!isc_time_isepoch(&refreshtime)) { + CHECK(putstr(text, "\nnext refresh: ")); + CHECK(putstr(text, rbuf)); + } + + if (!isc_time_isepoch(&expiretime)) { + CHECK(putstr(text, "\nexpires: ")); + CHECK(putstr(text, xbuf)); + } + + if (secure) { + CHECK(putstr(text, "\nsecure: yes")); + if (hasraw) { + CHECK(putstr(text, "\ninline signing: yes")); + } else { + CHECK(putstr(text, "\ninline signing: no")); + } + } else { + CHECK(putstr(text, "\nsecure: no")); + } + + if (maintain) { + CHECK(putstr(text, "\nkey maintenance: automatic")); + if (!isc_time_isepoch(&refreshkeytime)) { + CHECK(putstr(text, "\nnext key event: ")); + CHECK(putstr(text, kbuf)); + } + } else if (allow) { + CHECK(putstr(text, "\nkey maintenance: on command")); + } else if (secure || hasraw) { + CHECK(putstr(text, "\nkey maintenance: none")); + } + + if (!isc_time_isepoch(&resigntime)) { + CHECK(putstr(text, "\nnext resign node: ")); + CHECK(putstr(text, resignbuf)); + CHECK(putstr(text, "\nnext resign time: ")); + CHECK(putstr(text, rtbuf)); + } + + if (dynamic) { + CHECK(putstr(text, "\ndynamic: yes")); + if (frozen) { + CHECK(putstr(text, "\nfrozen: yes")); + } else { + CHECK(putstr(text, "\nfrozen: no")); + } + } else { + CHECK(putstr(text, "\ndynamic: no")); + } + + CHECK(putstr(text, "\nreconfigurable via modzone: ")); + CHECK(putstr(text, dns_zone_getadded(zone) ? "yes" : "no")); + +cleanup: + /* Indicate truncated output if possible. */ + if (result == ISC_R_NOSPACE) { + (void)putstr(text, "\n..."); + } + if ((result == ISC_R_SUCCESS || result == ISC_R_NOSPACE)) { + (void)putnull(text); + } + + if (db != NULL) { + dns_db_detach(&db); + } + if (rawdb != NULL) { + dns_db_detach(&rawdb); + } + if (incfiles != NULL && mayberaw != NULL) { + int i; + isc_mem_t *mctx = dns_zone_getmctx(mayberaw); + + for (i = 0; i < nfiles; i++) { + if (incfiles[i] != NULL) { + isc_mem_free(mctx, incfiles[i]); + } + } + isc_mem_free(mctx, incfiles); + } + if (raw != NULL) { + dns_zone_detach(&raw); + } + if (zone != NULL) { + dns_zone_detach(&zone); + } + return (result); +} + +isc_result_t +named_server_nta(named_server_t *server, isc_lex_t *lex, bool readonly, + isc_buffer_t **text) { + dns_view_t *view; + dns_ntatable_t *ntatable = NULL; + isc_result_t result = ISC_R_SUCCESS; + char *ptr, *nametext = NULL, *viewname; + char namebuf[DNS_NAME_FORMATSIZE]; + char viewbuf[DNS_NAME_FORMATSIZE]; + isc_stdtime_t now, when; + isc_time_t t; + char tbuf[64]; + const char *msg = NULL; + bool dump = false, force = false; + dns_fixedname_t fn; + const dns_name_t *ntaname; + dns_name_t *fname; + dns_ttl_t ntattl; + bool ttlset = false, excl = false, viewfound = false; + dns_rdataclass_t rdclass = dns_rdataclass_in; + bool first = true; + + REQUIRE(text != NULL); + + UNUSED(force); + + fname = dns_fixedname_initname(&fn); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + for (;;) { + /* Check for options */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcmp(ptr, "--") == 0) { + break; + } else if (argcheck(ptr, "dump")) { + dump = true; + } else if (argcheck(ptr, "remove")) { + ntattl = 0; + ttlset = true; + } else if (argcheck(ptr, "force")) { + force = true; + continue; + } else if (argcheck(ptr, "lifetime")) { + isc_textregion_t tr; + + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "No lifetime specified"; + CHECK(ISC_R_UNEXPECTEDEND); + } + + tr.base = ptr; + tr.length = strlen(ptr); + result = dns_ttl_fromtext(&tr, &ntattl); + if (result != ISC_R_SUCCESS) { + msg = "could not parse NTA lifetime"; + CHECK(result); + } + + if (ntattl > 604800) { + msg = "NTA lifetime cannot exceed one week"; + CHECK(ISC_R_RANGE); + } + + ttlset = true; + continue; + } else if (argcheck(ptr, "class")) { + isc_textregion_t tr; + + ptr = next_token(lex, text); + if (ptr == NULL) { + msg = "No class specified"; + CHECK(ISC_R_UNEXPECTEDEND); + } + + tr.base = ptr; + tr.length = strlen(ptr); + CHECK(dns_rdataclass_fromtext(&rdclass, &tr)); + continue; + } else if (ptr[0] == '-') { + msg = "Unknown option"; + CHECK(DNS_R_SYNTAX); + } else { + nametext = ptr; + } + + break; + } + + /* + * If -dump was specified, list NTA's and return + */ + if (dump) { + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + result = dns_view_getntatable(view, &ntatable); + if (result == ISC_R_NOTFOUND) { + continue; + } + + CHECK(dns_ntatable_totext(ntatable, view->name, text)); + } + CHECK(putnull(text)); + + goto cleanup; + } + + if (readonly) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_CONTROL, ISC_LOG_INFO, + "rejecting restricted control channel " + "NTA command"); + CHECK(ISC_R_FAILURE); + } + + /* Get the NTA name if not found above. */ + if (nametext == NULL) { + nametext = next_token(lex, text); + } + if (nametext == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Copy nametext as it'll be overwritten by next_token() */ + strlcpy(namebuf, nametext, DNS_NAME_FORMATSIZE); + + if (strcmp(namebuf, ".") == 0) { + ntaname = dns_rootname; + } else { + isc_buffer_t b; + isc_buffer_init(&b, namebuf, strlen(namebuf)); + isc_buffer_add(&b, strlen(namebuf)); + CHECK(dns_name_fromtext(fname, &b, dns_rootname, 0, NULL)); + ntaname = fname; + } + + /* Look for the view name. */ + viewname = next_token(lex, text); + if (viewname != NULL) { + strlcpy(viewbuf, viewname, DNS_NAME_FORMATSIZE); + viewname = viewbuf; + } + + if (next_token(lex, text) != NULL) { + CHECK(DNS_R_SYNTAX); + } + + isc_stdtime_get(&now); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + excl = true; + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewname != NULL && strcmp(view->name, viewname) != 0) { + continue; + } + viewfound = true; + + if (view->rdclass != rdclass && rdclass != dns_rdataclass_any) { + continue; + } + + if (view->nta_lifetime == 0) { + continue; + } + + if (!ttlset) { + ntattl = view->nta_lifetime; + } + + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + + result = dns_view_getntatable(view, &ntatable); + if (result == ISC_R_NOTFOUND) { + result = ISC_R_SUCCESS; + continue; + } + + result = dns_view_flushnode(view, ntaname, true); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "flush tree '%s' in cache view '%s': %s", namebuf, + view->name, isc_result_totext(result)); + + if (ntattl != 0) { + CHECK(dns_ntatable_add(ntatable, ntaname, force, now, + ntattl)); + + when = now + ntattl; + isc_time_set(&t, when, 0); + isc_time_formattimestamp(&t, tbuf, sizeof(tbuf)); + + if (!first) { + CHECK(putstr(text, "\n")); + } + first = false; + + CHECK(putstr(text, "Negative trust anchor added: ")); + CHECK(putstr(text, namebuf)); + CHECK(putstr(text, "/")); + CHECK(putstr(text, view->name)); + CHECK(putstr(text, ", expires ")); + CHECK(putstr(text, tbuf)); + + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "added NTA '%s' (%d sec) in view '%s'", + namebuf, ntattl, view->name); + } else { + bool wasremoved; + + result = dns_ntatable_delete(ntatable, ntaname); + if (result == ISC_R_SUCCESS) { + wasremoved = true; + } else if (result == ISC_R_NOTFOUND) { + wasremoved = false; + } else { + goto cleanup; + } + + if (!first) { + CHECK(putstr(text, "\n")); + } + first = false; + + CHECK(putstr(text, "Negative trust anchor ")); + CHECK(putstr(text, + wasremoved ? "removed: " : "not found: ")); + CHECK(putstr(text, namebuf)); + CHECK(putstr(text, "/")); + CHECK(putstr(text, view->name)); + + if (wasremoved) { + isc_log_write( + named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, + "removed NTA '%s' in view %s", namebuf, + view->name); + } + } + + result = dns_view_saventa(view); + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "error writing NTA file " + "for view '%s': %s", + view->name, isc_result_totext(result)); + } + } + + if (!viewfound) { + msg = "No such view"; + CHECK(ISC_R_NOTFOUND); + } + + (void)putnull(text); + +cleanup: + if (msg != NULL) { + (void)putstr(text, msg); + (void)putnull(text); + } + + if (excl) { + isc_task_endexclusive(server->task); + } + if (ntatable != NULL) { + dns_ntatable_detach(&ntatable); + } + return (result); +} + +isc_result_t +named_server_saventa(named_server_t *server) { + dns_view_t *view; + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + isc_result_t result = dns_view_saventa(view); + + if (result != ISC_R_SUCCESS) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "error writing NTA file " + "for view '%s': %s", + view->name, isc_result_totext(result)); + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +named_server_loadnta(named_server_t *server) { + dns_view_t *view; + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + isc_result_t result = dns_view_loadnta(view); + + if ((result != ISC_R_SUCCESS) && + (result != ISC_R_FILENOTFOUND) && + (result != ISC_R_NOTFOUND)) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "error loading NTA file " + "for view '%s': %s", + view->name, isc_result_totext(result)); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +mkey_refresh(dns_view_t *view, isc_buffer_t **text) { + isc_result_t result; + char msg[DNS_NAME_FORMATSIZE + 500] = ""; + + snprintf(msg, sizeof(msg), "refreshing managed keys for '%s'", + view->name); + CHECK(putstr(text, msg)); + CHECK(dns_zone_synckeyzone(view->managed_keys)); + +cleanup: + return (result); +} + +static isc_result_t +mkey_destroy(named_server_t *server, dns_view_t *view, isc_buffer_t **text) { + isc_result_t result; + char msg[DNS_NAME_FORMATSIZE + 500] = ""; + bool exclusive = false; + const char *file = NULL; + dns_db_t *dbp = NULL; + dns_zone_t *mkzone = NULL; + bool removed_a_file = false; + + if (view->managed_keys == NULL) { + CHECK(ISC_R_NOTFOUND); + } + + snprintf(msg, sizeof(msg), "destroying managed-keys database for '%s'", + view->name); + CHECK(putstr(text, msg)); + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + exclusive = true; + + /* Remove and clean up managed keys zone from view */ + mkzone = view->managed_keys; + view->managed_keys = NULL; + (void)dns_zone_flush(mkzone); + + /* Unload zone database */ + if (dns_zone_getdb(mkzone, &dbp) == ISC_R_SUCCESS) { + dns_db_detach(&dbp); + dns_zone_unload(mkzone); + } + + /* Delete files */ + file = dns_zone_getfile(mkzone); + result = isc_file_remove(file); + if (result == ISC_R_SUCCESS) { + removed_a_file = true; + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + + file = dns_zone_getjournal(mkzone); + result = isc_file_remove(file); + if (result == ISC_R_SUCCESS) { + removed_a_file = true; + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "file %s not removed: %s", file, + isc_result_totext(result)); + } + + if (!removed_a_file) { + CHECK(putstr(text, "error: no files could be removed")); + CHECK(ISC_R_FAILURE); + } + + dns_zone_detach(&mkzone); + result = ISC_R_SUCCESS; + +cleanup: + if (exclusive) { + isc_task_endexclusive(server->task); + } + return (result); +} + +static isc_result_t +mkey_dumpzone(dns_view_t *view, isc_buffer_t **text) { + isc_result_t result; + dns_db_t *db = NULL; + dns_dbversion_t *ver = NULL; + dns_rriterator_t rrit; + isc_stdtime_t now; + dns_name_t *prevname = NULL; + + isc_stdtime_get(&now); + + CHECK(dns_zone_getdb(view->managed_keys, &db)); + dns_db_currentversion(db, &ver); + dns_rriterator_init(&rrit, db, ver, 0); + for (result = dns_rriterator_first(&rrit); result == ISC_R_SUCCESS; + result = dns_rriterator_nextrrset(&rrit)) + { + char buf[DNS_NAME_FORMATSIZE + 500]; + dns_name_t *name = NULL; + dns_rdataset_t *kdset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_keydata_t kd; + uint32_t ttl; + + dns_rriterator_current(&rrit, &name, &ttl, &kdset, NULL); + if (kdset == NULL || kdset->type != dns_rdatatype_keydata || + !dns_rdataset_isassociated(kdset)) + { + continue; + } + + if (name != prevname) { + char nbuf[DNS_NAME_FORMATSIZE]; + dns_name_format(name, nbuf, sizeof(nbuf)); + snprintf(buf, sizeof(buf), "\n\n name: %s", nbuf); + CHECK(putstr(text, buf)); + } + + for (result = dns_rdataset_first(kdset); + result == ISC_R_SUCCESS; result = dns_rdataset_next(kdset)) + { + char alg[DNS_SECALG_FORMATSIZE]; + char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + dns_keytag_t keyid; + isc_region_t r; + isc_time_t t; + bool revoked; + + dns_rdata_reset(&rdata); + dns_rdataset_current(kdset, &rdata); + result = dns_rdata_tostruct(&rdata, &kd, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_rdata_toregion(&rdata, &r); + isc_region_consume(&r, 12); + keyid = dst_region_computeid(&r); + + snprintf(buf, sizeof(buf), "\n keyid: %u", keyid); + CHECK(putstr(text, buf)); + + dns_secalg_format(kd.algorithm, alg, sizeof(alg)); + snprintf(buf, sizeof(buf), "\n\talgorithm: %s", alg); + CHECK(putstr(text, buf)); + + revoked = ((kd.flags & DNS_KEYFLAG_REVOKE) != 0); + snprintf(buf, sizeof(buf), "\n\tflags:%s%s%s", + revoked ? " REVOKE" : "", + ((kd.flags & DNS_KEYFLAG_KSK) != 0) ? " SEP" + : "", + (kd.flags == 0) ? " (none)" : ""); + CHECK(putstr(text, buf)); + + isc_time_set(&t, kd.refresh, 0); + isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf)); + snprintf(buf, sizeof(buf), "\n\tnext refresh: %s", + tbuf); + CHECK(putstr(text, buf)); + + if (kd.removehd != 0) { + isc_time_set(&t, kd.removehd, 0); + isc_time_formathttptimestamp(&t, tbuf, + sizeof(tbuf)); + snprintf(buf, sizeof(buf), "\n\tremove at: %s", + tbuf); + CHECK(putstr(text, buf)); + } + + isc_time_set(&t, kd.addhd, 0); + isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf)); + if (kd.addhd == 0) { + snprintf(buf, sizeof(buf), "\n\tno trust"); + } else if (revoked) { + snprintf(buf, sizeof(buf), "\n\ttrust revoked"); + } else if (kd.addhd <= now) { + snprintf(buf, sizeof(buf), + "\n\ttrusted since: %s", tbuf); + } else if (kd.addhd > now) { + snprintf(buf, sizeof(buf), + "\n\ttrust pending: %s", tbuf); + } + CHECK(putstr(text, buf)); + } + } + + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + +cleanup: + if (ver != NULL) { + dns_rriterator_destroy(&rrit); + dns_db_closeversion(db, &ver, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + + return (result); +} + +static isc_result_t +mkey_status(dns_view_t *view, isc_buffer_t **text) { + isc_result_t result; + char msg[ISC_FORMATHTTPTIMESTAMP_SIZE]; + isc_time_t t; + + CHECK(putstr(text, "view: ")); + CHECK(putstr(text, view->name)); + + CHECK(putstr(text, "\nnext scheduled event: ")); + + dns_zone_getrefreshkeytime(view->managed_keys, &t); + if (isc_time_isepoch(&t)) { + CHECK(putstr(text, "never")); + } else { + isc_time_formathttptimestamp(&t, msg, sizeof(msg)); + CHECK(putstr(text, msg)); + } + + CHECK(mkey_dumpzone(view, text)); + +cleanup: + return (result); +} + +isc_result_t +named_server_mkeys(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + char *cmd, *classtxt, *viewtxt = NULL; + isc_result_t result = ISC_R_SUCCESS; + dns_view_t *view = NULL; + dns_rdataclass_t rdclass; + char msg[DNS_NAME_FORMATSIZE + 500] = ""; + enum { NONE, STATUS, REFRESH, SYNC, DESTROY } opt = NONE; + bool found = false; + bool first = true; + + REQUIRE(text != NULL); + + /* Skip rndc command name */ + cmd = next_token(lex, text); + if (cmd == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* Get managed-keys subcommand */ + cmd = next_token(lex, text); + if (cmd == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (strcasecmp(cmd, "status") == 0) { + opt = STATUS; + } else if (strcasecmp(cmd, "refresh") == 0) { + opt = REFRESH; + } else if (strcasecmp(cmd, "sync") == 0) { + opt = SYNC; + } else if (strcasecmp(cmd, "destroy") == 0) { + opt = DESTROY; + } else { + snprintf(msg, sizeof(msg), "unknown command '%s'", cmd); + (void)putstr(text, msg); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + + /* Look for the optional class name. */ + classtxt = next_token(lex, text); + if (classtxt != NULL) { + isc_textregion_t r; + r.base = classtxt; + r.length = strlen(classtxt); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + snprintf(msg, sizeof(msg), "unknown class '%s'", + classtxt); + (void)putstr(text, msg); + goto cleanup; + } + viewtxt = next_token(lex, text); + } + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (viewtxt != NULL && (rdclass != view->rdclass || + strcmp(view->name, viewtxt) != 0)) + { + continue; + } + + if (view->managed_keys == NULL) { + if (viewtxt != NULL) { + snprintf(msg, sizeof(msg), + "view '%s': no managed keys", viewtxt); + CHECK(putstr(text, msg)); + goto cleanup; + } else { + continue; + } + } + + found = true; + + switch (opt) { + case REFRESH: + if (!first) { + CHECK(putstr(text, "\n")); + } + CHECK(mkey_refresh(view, text)); + break; + case STATUS: + if (!first) { + CHECK(putstr(text, "\n\n")); + } + CHECK(mkey_status(view, text)); + break; + case SYNC: + CHECK(dns_zone_flush(view->managed_keys)); + break; + case DESTROY: + if (!first) { + CHECK(putstr(text, "\n")); + } + CHECK(mkey_destroy(server, view, text)); + break; + default: + UNREACHABLE(); + } + + if (viewtxt != NULL) { + break; + } + first = false; + } + + if (!found) { + CHECK(putstr(text, "no views with managed keys")); + } + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} + +isc_result_t +named_server_dnstap(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { +#ifdef HAVE_DNSTAP + char *ptr; + isc_result_t result; + bool reopen = false; + int backups = 0; + + REQUIRE(text != NULL); + + if (server->dtenv == NULL) { + return (ISC_R_NOTFOUND); + } + + /* Check the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + /* "dnstap-reopen" was used in 9.11.0b1 */ + if (strcasecmp(ptr, "dnstap-reopen") == 0) { + reopen = true; + } else { + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + } + + if (reopen || strcasecmp(ptr, "-reopen") == 0) { + backups = ISC_LOG_ROLLNEVER; + } else if ((strcasecmp(ptr, "-roll") == 0)) { + unsigned int n; + ptr = next_token(lex, text); + if (ptr != NULL) { + unsigned int u; + n = sscanf(ptr, "%u", &u); + if (n != 1U || u > INT_MAX) { + return (ISC_R_BADNUMBER); + } + backups = u; + } else { + backups = ISC_LOG_ROLLINFINITE; + } + } else { + return (DNS_R_SYNTAX); + } + + result = dns_dt_reopen(server->dtenv, backups); + return (result); +#else /* ifdef HAVE_DNSTAP */ + UNUSED(server); + UNUSED(lex); + UNUSED(text); + return (ISC_R_NOTIMPLEMENTED); +#endif /* ifdef HAVE_DNSTAP */ +} + +isc_result_t +named_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text) { + char *ptr; + isc_result_t result = ISC_R_SUCCESS; + uint32_t initial, idle, keepalive, advertised; + char msg[128]; + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + isc_nm_gettimeouts(named_g_netmgr, &initial, &idle, &keepalive, + &advertised); + + /* Look for optional arguments. */ + ptr = next_token(lex, NULL); + if (ptr != NULL) { + CHECK(isc_parse_uint32(&initial, ptr, 10)); + initial *= 100; + if (initial > MAX_INITIAL_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + if (initial < MIN_INITIAL_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + CHECK(isc_parse_uint32(&idle, ptr, 10)); + idle *= 100; + if (idle > MAX_IDLE_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + if (idle < MIN_IDLE_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + CHECK(isc_parse_uint32(&keepalive, ptr, 10)); + keepalive *= 100; + if (keepalive > MAX_KEEPALIVE_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + if (keepalive < MIN_KEEPALIVE_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + CHECK(isc_parse_uint32(&advertised, ptr, 10)); + advertised *= 100; + if (advertised > MAX_ADVERTISED_TIMEOUT) { + CHECK(ISC_R_RANGE); + } + + result = isc_task_beginexclusive(named_g_server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_nm_settimeouts(named_g_netmgr, initial, idle, keepalive, + advertised); + + isc_task_endexclusive(named_g_server->task); + } + + snprintf(msg, sizeof(msg), "tcp-initial-timeout=%u\n", initial / 100); + CHECK(putstr(text, msg)); + snprintf(msg, sizeof(msg), "tcp-idle-timeout=%u\n", idle / 100); + CHECK(putstr(text, msg)); + snprintf(msg, sizeof(msg), "tcp-keepalive-timeout=%u\n", + keepalive / 100); + CHECK(putstr(text, msg)); + snprintf(msg, sizeof(msg), "tcp-advertised-timeout=%u", + advertised / 100); + CHECK(putstr(text, msg)); + +cleanup: + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} + +isc_result_t +named_server_servestale(named_server_t *server, isc_lex_t *lex, + isc_buffer_t **text) { + char *ptr, *classtxt, *viewtxt = NULL; + char msg[128]; + dns_rdataclass_t rdclass = dns_rdataclass_in; + dns_view_t *view; + bool found = false; + dns_stale_answer_t staleanswersok = dns_stale_answer_conf; + bool wantstatus = false; + isc_result_t result = ISC_R_SUCCESS; + bool exclusive = false; + + REQUIRE(text != NULL); + + /* Skip the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + ptr = next_token(lex, NULL); + if (ptr == NULL) { + return (ISC_R_UNEXPECTEDEND); + } + + if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") || + !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) + { + staleanswersok = dns_stale_answer_yes; + } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") || + !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) + { + staleanswersok = dns_stale_answer_no; + } else if (strcasecmp(ptr, "reset") == 0) { + staleanswersok = dns_stale_answer_conf; + } else if (!strcasecmp(ptr, "check") || !strcasecmp(ptr, "status")) { + wantstatus = true; + } else { + return (DNS_R_SYNTAX); + } + + /* Look for the optional class name. */ + classtxt = next_token(lex, text); + if (classtxt != NULL) { + isc_textregion_t r; + + /* Look for the optional view name. */ + viewtxt = next_token(lex, text); + + /* + * If 'classtext' is not a valid class then it us a view name. + */ + r.base = classtxt; + r.length = strlen(classtxt); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + if (viewtxt != NULL) { + snprintf(msg, sizeof(msg), "unknown class '%s'", + classtxt); + (void)putstr(text, msg); + goto cleanup; + } + + viewtxt = classtxt; + classtxt = NULL; + } + } + + result = isc_task_beginexclusive(server->task); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + exclusive = true; + + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + dns_ttl_t stale_ttl = 0; + uint32_t stale_refresh = 0; + dns_db_t *db = NULL; + + if (classtxt != NULL && rdclass != view->rdclass) { + continue; + } + + if (viewtxt != NULL && strcmp(view->name, viewtxt) != 0) { + continue; + } + + if (!wantstatus) { + view->staleanswersok = staleanswersok; + found = true; + continue; + } + + db = NULL; + dns_db_attach(view->cachedb, &db); + (void)dns_db_getservestalettl(db, &stale_ttl); + (void)dns_db_getservestalerefresh(db, &stale_refresh); + dns_db_detach(&db); + if (found) { + CHECK(putstr(text, "\n")); + } + CHECK(putstr(text, view->name)); + CHECK(putstr(text, ": ")); + switch (view->staleanswersok) { + case dns_stale_answer_yes: + if (stale_ttl > 0) { + CHECK(putstr(text, "stale cache enabled; stale " + "answers enabled")); + } else { + CHECK(putstr(text, + "stale cache disabled; stale " + "answers unavailable")); + } + break; + case dns_stale_answer_no: + if (stale_ttl > 0) { + CHECK(putstr(text, "stale cache enabled; stale " + "answers disabled")); + } else { + CHECK(putstr(text, + "stale cache disabled; stale " + "answers unavailable")); + } + break; + case dns_stale_answer_conf: + if (view->staleanswersenable && stale_ttl > 0) { + CHECK(putstr(text, "stale cache enabled; stale " + "answers enabled")); + } else if (stale_ttl > 0) { + CHECK(putstr(text, "stale cache enabled; stale " + "answers disabled")); + } else { + CHECK(putstr(text, + "stale cache disabled; stale " + "answers unavailable")); + } + break; + } + if (stale_ttl > 0) { + snprintf(msg, sizeof(msg), + " (stale-answer-ttl=%u max-stale-ttl=%u " + "stale-refresh-time=%u)", + view->staleanswerttl, stale_ttl, + stale_refresh); + CHECK(putstr(text, msg)); + } + found = true; + } + + if (!found) { + result = ISC_R_NOTFOUND; + } + +cleanup: + if (exclusive) { + isc_task_endexclusive(named_g_server->task); + } + + if (isc_buffer_usedlength(*text) > 0) { + (void)putnull(text); + } + + return (result); +} diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c new file mode 100644 index 0000000..2c4760c --- /dev/null +++ b/bin/named/statschannel.c @@ -0,0 +1,4084 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#if HAVE_JSON_C +#include +#include +#endif /* HAVE_JSON_C */ + +#if HAVE_LIBXML2 +#include +#define ISC_XMLCHAR (const xmlChar *) +#endif /* HAVE_LIBXML2 */ + +#include "xsl_p.h" + +#define STATS_XML_VERSION_MAJOR "3" +#define STATS_XML_VERSION_MINOR "13" +#define STATS_XML_VERSION STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR + +#define STATS_JSON_VERSION_MAJOR "1" +#define STATS_JSON_VERSION_MINOR "7" +#define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR + +#define CHECK(m) \ + do { \ + result = (m); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +struct named_statschannel { + /* Unlocked */ + isc_httpdmgr_t *httpdmgr; + isc_sockaddr_t address; + isc_mem_t *mctx; + + /* + * Locked by channel lock: can be referenced and modified by both + * the server task and the channel task. + */ + isc_mutex_t lock; + dns_acl_t *acl; + + /* Locked by server task */ + ISC_LINK(struct named_statschannel) link; +}; + +typedef struct stats_dumparg { + isc_statsformat_t type; + void *arg; /* type dependent argument */ + int ncounters; /* for general statistics */ + int *counterindices; /* for general statistics */ + uint64_t *countervalues; /* for general statistics */ + isc_result_t result; +} stats_dumparg_t; + +static isc_once_t once = ISC_ONCE_INIT; + +#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) +#define EXTENDED_STATS +#else /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */ +#undef EXTENDED_STATS +#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */ + +#ifdef EXTENDED_STATS +static const char * +user_zonetype(dns_zone_t *zone) { + dns_zonetype_t ztype; + dns_view_t *view; + static const struct zt { + const dns_zonetype_t type; + const char *const string; + } typemap[] = { { dns_zone_none, "none" }, + { dns_zone_primary, "primary" }, + { dns_zone_secondary, "secondary" }, + { dns_zone_mirror, "mirror" }, + { dns_zone_stub, "stub" }, + { dns_zone_staticstub, "static-stub" }, + { dns_zone_key, "key" }, + { dns_zone_dlz, "dlz" }, + { dns_zone_redirect, "redirect" }, + { 0, NULL } }; + const struct zt *tp; + + if ((dns_zone_getoptions(zone) & DNS_ZONEOPT_AUTOEMPTY) != 0) { + return ("builtin"); + } + + view = dns_zone_getview(zone); + if (view != NULL && strcmp(view->name, "_bind") == 0) { + return ("builtin"); + } + + ztype = dns_zone_gettype(zone); + for (tp = typemap; tp->string != NULL && tp->type != ztype; tp++) { + /* empty */ + } + return (tp->string); +} +#endif /* ifdef EXTENDED_STATS */ + +/*% + * Statistics descriptions. These could be statistically initialized at + * compile time, but we configure them run time in the init_desc() function + * below so that they'll be less susceptible to counter name changes. + */ +static const char *nsstats_desc[ns_statscounter_max]; +static const char *resstats_desc[dns_resstatscounter_max]; +static const char *adbstats_desc[dns_adbstats_max]; +static const char *zonestats_desc[dns_zonestatscounter_max]; +static const char *sockstats_desc[isc_sockstatscounter_max]; +static const char *dnssecstats_desc[dns_dnssecstats_max]; +static const char *udpinsizestats_desc[dns_sizecounter_in_max]; +static const char *udpoutsizestats_desc[dns_sizecounter_out_max]; +static const char *tcpinsizestats_desc[dns_sizecounter_in_max]; +static const char *tcpoutsizestats_desc[dns_sizecounter_out_max]; +static const char *dnstapstats_desc[dns_dnstapcounter_max]; +static const char *gluecachestats_desc[dns_gluecachestatscounter_max]; +#if defined(EXTENDED_STATS) +static const char *nsstats_xmldesc[ns_statscounter_max]; +static const char *resstats_xmldesc[dns_resstatscounter_max]; +static const char *adbstats_xmldesc[dns_adbstats_max]; +static const char *zonestats_xmldesc[dns_zonestatscounter_max]; +static const char *sockstats_xmldesc[isc_sockstatscounter_max]; +static const char *dnssecstats_xmldesc[dns_dnssecstats_max]; +static const char *udpinsizestats_xmldesc[dns_sizecounter_in_max]; +static const char *udpoutsizestats_xmldesc[dns_sizecounter_out_max]; +static const char *tcpinsizestats_xmldesc[dns_sizecounter_in_max]; +static const char *tcpoutsizestats_xmldesc[dns_sizecounter_out_max]; +static const char *dnstapstats_xmldesc[dns_dnstapcounter_max]; +static const char *gluecachestats_xmldesc[dns_gluecachestatscounter_max]; +#else /* if defined(EXTENDED_STATS) */ +#define nsstats_xmldesc NULL +#define resstats_xmldesc NULL +#define adbstats_xmldesc NULL +#define zonestats_xmldesc NULL +#define sockstats_xmldesc NULL +#define dnssecstats_xmldesc NULL +#define udpinsizestats_xmldesc NULL +#define udpoutsizestats_xmldesc NULL +#define tcpinsizestats_xmldesc NULL +#define tcpoutsizestats_xmldesc NULL +#define dnstapstats_xmldesc NULL +#define gluecachestats_xmldesc NULL +#endif /* EXTENDED_STATS */ + +#define TRY0(a) \ + do { \ + xmlrc = (a); \ + if (xmlrc < 0) \ + goto cleanup; \ + } while (0) + +/*% + * Mapping arrays to represent statistics counters in the order of our + * preference, regardless of the order of counter indices. For example, + * nsstats_desc[nsstats_index[0]] will be the description that is shown first. + */ +static int nsstats_index[ns_statscounter_max]; +static int resstats_index[dns_resstatscounter_max]; +static int adbstats_index[dns_adbstats_max]; +static int zonestats_index[dns_zonestatscounter_max]; +static int sockstats_index[isc_sockstatscounter_max]; +static int dnssecstats_index[dns_dnssecstats_max]; +static int udpinsizestats_index[dns_sizecounter_in_max]; +static int udpoutsizestats_index[dns_sizecounter_out_max]; +static int tcpinsizestats_index[dns_sizecounter_in_max]; +static int tcpoutsizestats_index[dns_sizecounter_out_max]; +static int dnstapstats_index[dns_dnstapcounter_max]; +static int gluecachestats_index[dns_gluecachestatscounter_max]; + +static void +set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs, + const char *xdesc, const char **xdescs) { + REQUIRE(counter < maxcounter); + REQUIRE(fdescs != NULL && fdescs[counter] == NULL); +#if defined(EXTENDED_STATS) + REQUIRE(xdescs != NULL && xdescs[counter] == NULL); +#endif /* if defined(EXTENDED_STATS) */ + + fdescs[counter] = fdesc; +#if defined(EXTENDED_STATS) + xdescs[counter] = xdesc; +#else /* if defined(EXTENDED_STATS) */ + UNUSED(xdesc); + UNUSED(xdescs); +#endif /* if defined(EXTENDED_STATS) */ +} + +static void +init_desc(void) { + int i; + + /* Initialize name server statistics */ + for (i = 0; i < ns_statscounter_max; i++) { + nsstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < ns_statscounter_max; i++) { + nsstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_NSSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(ns_statscounter_##counterid, ns_statscounter_max, \ + desc, nsstats_desc, xmldesc, nsstats_xmldesc); \ + nsstats_index[i++] = ns_statscounter_##counterid; \ + } while (0) + + i = 0; + SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4"); + SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6"); + SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0"); + SET_NSSTATDESC(badednsver, + "requests with unsupported EDNS version received", + "ReqBadEDNSVer"); + SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG"); + SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0"); + SET_NSSTATDESC(invalidsig, "requests with invalid signature", + "ReqBadSIG"); + SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP"); + SET_NSSTATDESC(tcphighwater, "TCP connection high-water", + "TCPConnHighWater"); + SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej"); + SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej"); + SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej"); + SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej"); + SET_NSSTATDESC(response, "responses sent", "Response"); + SET_NSSTATDESC(truncatedresp, "truncated responses sent", + "TruncatedResp"); + SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0"); + SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG"); + SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0"); + SET_NSSTATDESC(success, "queries resulted in successful answer", + "QrySuccess"); + SET_NSSTATDESC(authans, "queries resulted in authoritative answer", + "QryAuthAns"); + SET_NSSTATDESC(nonauthans, + "queries resulted in non authoritative answer", + "QryNoauthAns"); + SET_NSSTATDESC(referral, "queries resulted in referral answer", + "QryReferral"); + SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset"); + SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL"); + SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR"); + SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN"); + SET_NSSTATDESC(recursion, "queries caused recursion", "QryRecursion"); + SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate"); + SET_NSSTATDESC(dropped, "queries dropped", "QryDropped"); + SET_NSSTATDESC(failure, "other query failures", "QryFailure"); + SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone"); + SET_NSSTATDESC(updatereqfwd, "update requests forwarded", + "UpdateReqFwd"); + SET_NSSTATDESC(updaterespfwd, "update responses forwarded", + "UpdateRespFwd"); + SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail"); + SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone"); + SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail"); + SET_NSSTATDESC(updatebadprereq, + "updates rejected due to prerequisite failure", + "UpdateBadPrereq"); + SET_NSSTATDESC(recursclients, "recursing clients", "RecursClients"); + SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64"); + SET_NSSTATDESC(ratedropped, "responses dropped for rate limits", + "RateDropped"); + SET_NSSTATDESC(rateslipped, "responses truncated for rate limits", + "RateSlipped"); + SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites", + "RPZRewrites"); + SET_NSSTATDESC(udp, "UDP queries received", "QryUDP"); + SET_NSSTATDESC(tcp, "TCP queries received", "QryTCP"); + SET_NSSTATDESC(nsidopt, "NSID option received", "NSIDOpt"); + SET_NSSTATDESC(expireopt, "Expire option received", "ExpireOpt"); + SET_NSSTATDESC(keepaliveopt, "EDNS TCP keepalive option received", + "KeepAliveOpt"); + SET_NSSTATDESC(padopt, "EDNS padding option received", "PadOpt"); + SET_NSSTATDESC(otheropt, "Other EDNS option received", "OtherOpt"); + SET_NSSTATDESC(cookiein, "COOKIE option received", "CookieIn"); + SET_NSSTATDESC(cookienew, "COOKIE - client only", "CookieNew"); + SET_NSSTATDESC(cookiebadsize, "COOKIE - bad size", "CookieBadSize"); + SET_NSSTATDESC(cookiebadtime, "COOKIE - bad time", "CookieBadTime"); + SET_NSSTATDESC(cookienomatch, "COOKIE - no match", "CookieNoMatch"); + SET_NSSTATDESC(cookiematch, "COOKIE - match", "CookieMatch"); + SET_NSSTATDESC(ecsopt, "EDNS client subnet option received", "ECSOpt"); + SET_NSSTATDESC(nxdomainredirect, + "queries resulted in NXDOMAIN that were redirected", + "QryNXRedir"); + SET_NSSTATDESC(nxdomainredirect_rlookup, + "queries resulted in NXDOMAIN that were redirected and " + "resulted in a successful remote lookup", + "QryNXRedirRLookup"); + SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE"); + SET_NSSTATDESC(nxdomainsynth, "synthesized a NXDOMAIN response", + "SynthNXDOMAIN"); + SET_NSSTATDESC(nodatasynth, "synthesized a no-data response", + "SynthNODATA"); + SET_NSSTATDESC(wildcardsynth, "synthesized a wildcard response", + "SynthWILDCARD"); + SET_NSSTATDESC(trystale, + "attempts to use stale cache data after lookup failure", + "QryTryStale"); + SET_NSSTATDESC(usedstale, + "successful uses of stale cache data after lookup " + "failure", + "QryUsedStale"); + SET_NSSTATDESC(prefetch, "queries triggered prefetch", "Prefetch"); + SET_NSSTATDESC(keytagopt, "Keytag option received", "KeyTagOpt"); + SET_NSSTATDESC(reclimitdropped, + "queries dropped due to recursive client limit", + "RecLimitDropped"); + SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota"); + + INSIST(i == ns_statscounter_max); + + /* Initialize resolver statistics */ + for (i = 0; i < dns_resstatscounter_max; i++) { + resstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < dns_resstatscounter_max; i++) { + resstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_RESSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(dns_resstatscounter_##counterid, \ + dns_resstatscounter_max, desc, resstats_desc, \ + xmldesc, resstats_xmldesc); \ + resstats_index[i++] = dns_resstatscounter_##counterid; \ + } while (0) + + i = 0; + SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4"); + SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6"); + SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4"); + SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6"); + SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN"); + SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL"); + SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR"); + SET_RESSTATDESC(othererror, "other errors received", "OtherError"); + SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail"); + SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch"); + SET_RESSTATDESC(truncated, "truncated responses received", "Truncated"); + SET_RESSTATDESC(lame, "lame delegations received", "Lame"); + SET_RESSTATDESC(retry, "query retries", "Retry"); + SET_RESSTATDESC(dispabort, "queries aborted due to quota", + "QueryAbort"); + SET_RESSTATDESC(dispsockfail, "failures in opening query sockets", + "QuerySockFail"); + SET_RESSTATDESC(disprequdp, "UDP queries in progress", "QueryCurUDP"); + SET_RESSTATDESC(dispreqtcp, "TCP queries in progress", "QueryCurTCP"); + SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout"); + SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4"); + SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6"); + SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed", + "GlueFetchv4Fail"); + SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed", + "GlueFetchv6Fail"); + SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt"); + SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk"); + SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded", + "ValNegOk"); + SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail"); + SET_RESSTATDESC(queryrtt0, + "queries with RTT < " DNS_RESOLVER_QRYRTTCLASS0STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR); + SET_RESSTATDESC(queryrtt1, + "queries with RTT " DNS_RESOLVER_QRYRTTCLASS0STR + "-" DNS_RESOLVER_QRYRTTCLASS1STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR); + SET_RESSTATDESC(queryrtt2, + "queries with RTT " DNS_RESOLVER_QRYRTTCLASS1STR + "-" DNS_RESOLVER_QRYRTTCLASS2STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR); + SET_RESSTATDESC(queryrtt3, + "queries with RTT " DNS_RESOLVER_QRYRTTCLASS2STR + "-" DNS_RESOLVER_QRYRTTCLASS3STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR); + SET_RESSTATDESC(queryrtt4, + "queries with RTT " DNS_RESOLVER_QRYRTTCLASS3STR + "-" DNS_RESOLVER_QRYRTTCLASS4STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR); + SET_RESSTATDESC(queryrtt5, + "queries with RTT > " DNS_RESOLVER_QRYRTTCLASS4STR "ms", + "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+"); + SET_RESSTATDESC(nfetch, "active fetches", "NumFetch"); + SET_RESSTATDESC(buckets, "bucket size", "BucketSize"); + SET_RESSTATDESC(refused, "REFUSED received", "REFUSED"); + SET_RESSTATDESC(cookienew, "COOKIE send with client cookie only", + "ClientCookieOut"); + SET_RESSTATDESC(cookieout, "COOKIE sent with client and server cookie", + "ServerCookieOut"); + SET_RESSTATDESC(cookiein, "COOKIE replies received", "CookieIn"); + SET_RESSTATDESC(cookieok, "COOKIE client ok", "CookieClientOk"); + SET_RESSTATDESC(badvers, "bad EDNS version", "BadEDNSVersion"); + SET_RESSTATDESC(badcookie, "bad cookie rcode", "BadCookieRcode"); + SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota"); + SET_RESSTATDESC(serverquota, "spilled due to server quota", + "ServerQuota"); + SET_RESSTATDESC(clientquota, "spilled due to clients per query quota", + "ClientQuota"); + SET_RESSTATDESC(nextitem, "waited for next item", "NextItem"); + SET_RESSTATDESC(priming, "priming queries", "Priming"); + + INSIST(i == dns_resstatscounter_max); + + /* Initialize adb statistics */ + for (i = 0; i < dns_adbstats_max; i++) { + adbstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < dns_adbstats_max; i++) { + adbstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_ADBSTATDESC(id, desc, xmldesc) \ + do { \ + set_desc(dns_adbstats_##id, dns_adbstats_max, desc, \ + adbstats_desc, xmldesc, adbstats_xmldesc); \ + adbstats_index[i++] = dns_adbstats_##id; \ + } while (0) + i = 0; + SET_ADBSTATDESC(nentries, "Address hash table size", "nentries"); + SET_ADBSTATDESC(entriescnt, "Addresses in hash table", "entriescnt"); + SET_ADBSTATDESC(nnames, "Name hash table size", "nnames"); + SET_ADBSTATDESC(namescnt, "Names in hash table", "namescnt"); + + INSIST(i == dns_adbstats_max); + + /* Initialize zone statistics */ + for (i = 0; i < dns_zonestatscounter_max; i++) { + zonestats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < dns_zonestatscounter_max; i++) { + zonestats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_ZONESTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(dns_zonestatscounter_##counterid, \ + dns_zonestatscounter_max, desc, zonestats_desc, \ + xmldesc, zonestats_xmldesc); \ + zonestats_index[i++] = dns_zonestatscounter_##counterid; \ + } while (0) + + i = 0; + SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4"); + SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6"); + SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4"); + SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6"); + SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej"); + SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4"); + SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6"); + SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4"); + SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6"); + SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4"); + SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6"); + SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded", + "XfrSuccess"); + SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail"); + INSIST(i == dns_zonestatscounter_max); + + /* Initialize socket statistics */ + for (i = 0; i < isc_sockstatscounter_max; i++) { + sockstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < isc_sockstatscounter_max; i++) { + sockstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_SOCKSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(isc_sockstatscounter_##counterid, \ + isc_sockstatscounter_max, desc, sockstats_desc, \ + xmldesc, sockstats_xmldesc); \ + sockstats_index[i++] = isc_sockstatscounter_##counterid; \ + } while (0) + + i = 0; + SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open"); + SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open"); + SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open"); + SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open"); + SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen"); + SET_SOCKSTATDESC(rawopen, "Raw sockets opened", "RawOpen"); + SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures", + "UDP4OpenFail"); + SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures", + "UDP6OpenFail"); + SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures", + "TCP4OpenFail"); + SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures", + "TCP6OpenFail"); + SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures", + "UnixOpenFail"); + SET_SOCKSTATDESC(rawopenfail, "Raw socket open failures", + "RawOpenFail"); + SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close"); + SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close"); + SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close"); + SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close"); + SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose"); + SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed", + "FDWatchClose"); + SET_SOCKSTATDESC(rawclose, "Raw sockets closed", "RawClose"); + SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures", + "UDP4BindFail"); + SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures", + "UDP6BindFail"); + SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures", + "TCP4BindFail"); + SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures", + "TCP6BindFail"); + SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures", + "UnixBindFail"); + SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures", + "FdwatchBindFail"); + SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures", + "UDP4ConnFail"); + SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures", + "UDP6ConnFail"); + SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures", + "TCP4ConnFail"); + SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures", + "TCP6ConnFail"); + SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures", + "UnixConnFail"); + SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures", + "FDwatchConnFail"); + SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established", + "UDP4Conn"); + SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established", + "UDP6Conn"); + SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established", + "TCP4Conn"); + SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established", + "TCP6Conn"); + SET_SOCKSTATDESC(unixconnect, "Unix domain connections established", + "UnixConn"); + SET_SOCKSTATDESC(fdwatchconnect, + "FDwatch domain connections established", + "FDwatchConn"); + SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures", + "TCP4AcceptFail"); + SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures", + "TCP6AcceptFail"); + SET_SOCKSTATDESC(unixacceptfail, + "Unix domain connection accept failures", + "UnixAcceptFail"); + SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted", + "TCP4Accept"); + SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted", + "TCP6Accept"); + SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted", + "UnixAccept"); + SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr"); + SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr"); + SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr"); + SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr"); + SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors", + "UnixSendErr"); + SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors", + "FDwatchSendErr"); + SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr"); + SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr"); + SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr"); + SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr"); + SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors", + "UnixRecvErr"); + SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors", + "FDwatchRecvErr"); + SET_SOCKSTATDESC(rawrecvfail, "Raw recv errors", "RawRecvErr"); + SET_SOCKSTATDESC(udp4active, "UDP/IPv4 sockets active", "UDP4Active"); + SET_SOCKSTATDESC(udp6active, "UDP/IPv6 sockets active", "UDP6Active"); + SET_SOCKSTATDESC(tcp4active, "TCP/IPv4 sockets active", "TCP4Active"); + SET_SOCKSTATDESC(tcp6active, "TCP/IPv6 sockets active", "TCP6Active"); + SET_SOCKSTATDESC(unixactive, "Unix domain sockets active", + "UnixActive"); + SET_SOCKSTATDESC(rawactive, "Raw sockets active", "RawActive"); + INSIST(i == isc_sockstatscounter_max); + + /* Initialize DNSSEC statistics */ + for (i = 0; i < dns_dnssecstats_max; i++) { + dnssecstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < dns_dnssecstats_max; i++) { + dnssecstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_DNSSECSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(dns_dnssecstats_##counterid, dns_dnssecstats_max, \ + desc, dnssecstats_desc, xmldesc, \ + dnssecstats_xmldesc); \ + dnssecstats_index[i++] = dns_dnssecstats_##counterid; \ + } while (0) + + i = 0; + SET_DNSSECSTATDESC(asis, + "dnssec validation success with signer " + "\"as is\"", + "DNSSECasis"); + SET_DNSSECSTATDESC(downcase, + "dnssec validation success with signer " + "lower cased", + "DNSSECdowncase"); + SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature", + "DNSSECwild"); + SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail"); + INSIST(i == dns_dnssecstats_max); + + /* Initialize dnstap statistics */ + for (i = 0; i < dns_dnstapcounter_max; i++) { + dnstapstats_desc[i] = NULL; + } +#if defined(EXTENDED_STATS) + for (i = 0; i < dns_dnstapcounter_max; i++) { + dnstapstats_xmldesc[i] = NULL; + } +#endif /* if defined(EXTENDED_STATS) */ + +#define SET_DNSTAPSTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(dns_dnstapcounter_##counterid, dns_dnstapcounter_max, \ + desc, dnstapstats_desc, xmldesc, \ + dnstapstats_xmldesc); \ + dnstapstats_index[i++] = dns_dnstapcounter_##counterid; \ + } while (0) + i = 0; + SET_DNSTAPSTATDESC(success, "dnstap messages written", "DNSTAPsuccess"); + SET_DNSTAPSTATDESC(drop, "dnstap messages dropped", "DNSTAPdropped"); + INSIST(i == dns_dnstapcounter_max); + +#define SET_GLUECACHESTATDESC(counterid, desc, xmldesc) \ + do { \ + set_desc(dns_gluecachestatscounter_##counterid, \ + dns_gluecachestatscounter_max, desc, \ + gluecachestats_desc, xmldesc, \ + gluecachestats_xmldesc); \ + gluecachestats_index[i++] = \ + dns_gluecachestatscounter_##counterid; \ + } while (0) + i = 0; + SET_GLUECACHESTATDESC(hits_present, "Hits for present glue (cached)", + "GLUECACHEhitspresent"); + SET_GLUECACHESTATDESC(hits_absent, + "Hits for non-existent glue (cached)", + "GLUECACHEhitsabsent"); + SET_GLUECACHESTATDESC(inserts_present, + "Miss-plus-cache-inserts for present glue", + "GLUECACHEinsertspresent"); + SET_GLUECACHESTATDESC(inserts_absent, + "Miss-plus-cache-inserts for non-existent glue", + "GLUECACHEinsertsabsent"); + INSIST(i == dns_gluecachestatscounter_max); + + /* Sanity check */ + for (i = 0; i < ns_statscounter_max; i++) { + INSIST(nsstats_desc[i] != NULL); + } + for (i = 0; i < dns_resstatscounter_max; i++) { + INSIST(resstats_desc[i] != NULL); + } + for (i = 0; i < dns_adbstats_max; i++) { + INSIST(adbstats_desc[i] != NULL); + } + for (i = 0; i < dns_zonestatscounter_max; i++) { + INSIST(zonestats_desc[i] != NULL); + } + for (i = 0; i < isc_sockstatscounter_max; i++) { + INSIST(sockstats_desc[i] != NULL); + } + for (i = 0; i < dns_dnssecstats_max; i++) { + INSIST(dnssecstats_desc[i] != NULL); + } + for (i = 0; i < dns_dnstapcounter_max; i++) { + INSIST(dnstapstats_desc[i] != NULL); + } + for (i = 0; i < dns_gluecachestatscounter_max; i++) { + INSIST(gluecachestats_desc[i] != NULL); + } +#if defined(EXTENDED_STATS) + for (i = 0; i < ns_statscounter_max; i++) { + INSIST(nsstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_resstatscounter_max; i++) { + INSIST(resstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_adbstats_max; i++) { + INSIST(adbstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_zonestatscounter_max; i++) { + INSIST(zonestats_xmldesc[i] != NULL); + } + for (i = 0; i < isc_sockstatscounter_max; i++) { + INSIST(sockstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_dnssecstats_max; i++) { + INSIST(dnssecstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_dnstapcounter_max; i++) { + INSIST(dnstapstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_gluecachestatscounter_max; i++) { + INSIST(gluecachestats_xmldesc[i] != NULL); + } +#endif /* if defined(EXTENDED_STATS) */ + + /* Initialize traffic size statistics */ + for (i = 0; i < dns_sizecounter_in_max; i++) { + udpinsizestats_desc[i] = NULL; + tcpinsizestats_desc[i] = NULL; +#if defined(EXTENDED_STATS) + udpinsizestats_xmldesc[i] = NULL; + tcpinsizestats_xmldesc[i] = NULL; +#endif /* if defined(EXTENDED_STATS) */ + } + for (i = 0; i < dns_sizecounter_out_max; i++) { + udpoutsizestats_desc[i] = NULL; + tcpoutsizestats_desc[i] = NULL; +#if defined(EXTENDED_STATS) + udpoutsizestats_xmldesc[i] = NULL; + tcpoutsizestats_xmldesc[i] = NULL; +#endif /* if defined(EXTENDED_STATS) */ + } + +#define SET_SIZESTATDESC(counterid, desc, xmldesc, inout) \ + do { \ + set_desc(dns_sizecounter_##inout##_##counterid, \ + dns_sizecounter_##inout##_max, desc, \ + udp##inout##sizestats_desc, xmldesc, \ + udp##inout##sizestats_xmldesc); \ + set_desc(dns_sizecounter_##inout##_##counterid, \ + dns_sizecounter_##inout##_max, desc, \ + tcp##inout##sizestats_desc, xmldesc, \ + tcp##inout##sizestats_xmldesc); \ + udp##inout##sizestats_index[i] = \ + dns_sizecounter_##inout##_##counterid; \ + tcp##inout##sizestats_index[i] = \ + dns_sizecounter_##inout##_##counterid; \ + i++; \ + } while (0) + + i = 0; + SET_SIZESTATDESC(0, "requests received 0-15 bytes", "0-15", in); + SET_SIZESTATDESC(16, "requests received 16-31 bytes", "16-31", in); + SET_SIZESTATDESC(32, "requests received 32-47 bytes", "32-47", in); + SET_SIZESTATDESC(48, "requests received 48-63 bytes", "48-63", in); + SET_SIZESTATDESC(64, "requests received 64-79 bytes", "64-79", in); + SET_SIZESTATDESC(80, "requests received 80-95 bytes", "80-95", in); + SET_SIZESTATDESC(96, "requests received 96-111 bytes", "96-111", in); + SET_SIZESTATDESC(112, "requests received 112-127 bytes", "112-127", in); + SET_SIZESTATDESC(128, "requests received 128-143 bytes", "128-143", in); + SET_SIZESTATDESC(144, "requests received 144-159 bytes", "144-159", in); + SET_SIZESTATDESC(160, "requests received 160-175 bytes", "160-175", in); + SET_SIZESTATDESC(176, "requests received 176-191 bytes", "176-191", in); + SET_SIZESTATDESC(192, "requests received 192-207 bytes", "192-207", in); + SET_SIZESTATDESC(208, "requests received 208-223 bytes", "208-223", in); + SET_SIZESTATDESC(224, "requests received 224-239 bytes", "224-239", in); + SET_SIZESTATDESC(240, "requests received 240-255 bytes", "240-255", in); + SET_SIZESTATDESC(256, "requests received 256-271 bytes", "256-271", in); + SET_SIZESTATDESC(272, "requests received 272-287 bytes", "272-287", in); + SET_SIZESTATDESC(288, "requests received 288+ bytes", "288+", in); + INSIST(i == dns_sizecounter_in_max); + + i = 0; + SET_SIZESTATDESC(0, "responses sent 0-15 bytes", "0-15", out); + SET_SIZESTATDESC(16, "responses sent 16-31 bytes", "16-31", out); + SET_SIZESTATDESC(32, "responses sent 32-47 bytes", "32-47", out); + SET_SIZESTATDESC(48, "responses sent 48-63 bytes", "48-63", out); + SET_SIZESTATDESC(64, "responses sent 64-79 bytes", "64-79", out); + SET_SIZESTATDESC(80, "responses sent 80-95 bytes", "80-95", out); + SET_SIZESTATDESC(96, "responses sent 96-111 bytes", "96-111", out); + SET_SIZESTATDESC(112, "responses sent 112-127 bytes", "112-127", out); + SET_SIZESTATDESC(128, "responses sent 128-143 bytes", "128-143", out); + SET_SIZESTATDESC(144, "responses sent 144-159 bytes", "144-159", out); + SET_SIZESTATDESC(160, "responses sent 160-175 bytes", "160-175", out); + SET_SIZESTATDESC(176, "responses sent 176-191 bytes", "176-191", out); + SET_SIZESTATDESC(192, "responses sent 192-207 bytes", "192-207", out); + SET_SIZESTATDESC(208, "responses sent 208-223 bytes", "208-223", out); + SET_SIZESTATDESC(224, "responses sent 224-239 bytes", "224-239", out); + SET_SIZESTATDESC(240, "responses sent 240-255 bytes", "240-255", out); + SET_SIZESTATDESC(256, "responses sent 256-271 bytes", "256-271", out); + SET_SIZESTATDESC(272, "responses sent 272-287 bytes", "272-287", out); + SET_SIZESTATDESC(288, "responses sent 288-303 bytes", "288-303", out); + SET_SIZESTATDESC(304, "responses sent 304-319 bytes", "304-319", out); + SET_SIZESTATDESC(320, "responses sent 320-335 bytes", "320-335", out); + SET_SIZESTATDESC(336, "responses sent 336-351 bytes", "336-351", out); + SET_SIZESTATDESC(352, "responses sent 352-367 bytes", "352-367", out); + SET_SIZESTATDESC(368, "responses sent 368-383 bytes", "368-383", out); + SET_SIZESTATDESC(384, "responses sent 384-399 bytes", "384-399", out); + SET_SIZESTATDESC(400, "responses sent 400-415 bytes", "400-415", out); + SET_SIZESTATDESC(416, "responses sent 416-431 bytes", "416-431", out); + SET_SIZESTATDESC(432, "responses sent 432-447 bytes", "432-447", out); + SET_SIZESTATDESC(448, "responses sent 448-463 bytes", "448-463", out); + SET_SIZESTATDESC(464, "responses sent 464-479 bytes", "464-479", out); + SET_SIZESTATDESC(480, "responses sent 480-495 bytes", "480-495", out); + SET_SIZESTATDESC(496, "responses sent 496-511 bytes", "496-511", out); + SET_SIZESTATDESC(512, "responses sent 512-527 bytes", "512-527", out); + SET_SIZESTATDESC(528, "responses sent 528-543 bytes", "528-543", out); + SET_SIZESTATDESC(544, "responses sent 544-559 bytes", "544-559", out); + SET_SIZESTATDESC(560, "responses sent 560-575 bytes", "560-575", out); + SET_SIZESTATDESC(576, "responses sent 576-591 bytes", "576-591", out); + SET_SIZESTATDESC(592, "responses sent 592-607 bytes", "592-607", out); + SET_SIZESTATDESC(608, "responses sent 608-623 bytes", "608-623", out); + SET_SIZESTATDESC(624, "responses sent 624-639 bytes", "624-639", out); + SET_SIZESTATDESC(640, "responses sent 640-655 bytes", "640-655", out); + SET_SIZESTATDESC(656, "responses sent 656-671 bytes", "656-671", out); + SET_SIZESTATDESC(672, "responses sent 672-687 bytes", "672-687", out); + SET_SIZESTATDESC(688, "responses sent 688-703 bytes", "688-703", out); + SET_SIZESTATDESC(704, "responses sent 704-719 bytes", "704-719", out); + SET_SIZESTATDESC(720, "responses sent 720-735 bytes", "720-735", out); + SET_SIZESTATDESC(736, "responses sent 736-751 bytes", "736-751", out); + SET_SIZESTATDESC(752, "responses sent 752-767 bytes", "752-767", out); + SET_SIZESTATDESC(768, "responses sent 768-783 bytes", "768-783", out); + SET_SIZESTATDESC(784, "responses sent 784-799 bytes", "784-799", out); + SET_SIZESTATDESC(800, "responses sent 800-815 bytes", "800-815", out); + SET_SIZESTATDESC(816, "responses sent 816-831 bytes", "816-831", out); + SET_SIZESTATDESC(832, "responses sent 832-847 bytes", "832-847", out); + SET_SIZESTATDESC(848, "responses sent 848-863 bytes", "848-863", out); + SET_SIZESTATDESC(864, "responses sent 864-879 bytes", "864-879", out); + SET_SIZESTATDESC(880, "responses sent 880-895 bytes", "880-895", out); + SET_SIZESTATDESC(896, "responses sent 896-911 bytes", "896-911", out); + SET_SIZESTATDESC(912, "responses sent 912-927 bytes", "912-927", out); + SET_SIZESTATDESC(928, "responses sent 928-943 bytes", "928-943", out); + SET_SIZESTATDESC(944, "responses sent 944-959 bytes", "944-959", out); + SET_SIZESTATDESC(960, "responses sent 960-975 bytes", "960-975", out); + SET_SIZESTATDESC(976, "responses sent 976-991 bytes", "976-991", out); + SET_SIZESTATDESC(992, "responses sent 992-1007 bytes", "992-1007", out); + SET_SIZESTATDESC(1008, "responses sent 1008-1023 bytes", "1008-1023", + out); + SET_SIZESTATDESC(1024, "responses sent 1024-1039 bytes", "1024-1039", + out); + SET_SIZESTATDESC(1040, "responses sent 1040-1055 bytes", "1040-1055", + out); + SET_SIZESTATDESC(1056, "responses sent 1056-1071 bytes", "1056-1071", + out); + SET_SIZESTATDESC(1072, "responses sent 1072-1087 bytes", "1072-1087", + out); + SET_SIZESTATDESC(1088, "responses sent 1088-1103 bytes", "1088-1103", + out); + SET_SIZESTATDESC(1104, "responses sent 1104-1119 bytes", "1104-1119", + out); + SET_SIZESTATDESC(1120, "responses sent 1120-1135 bytes", "1120-1135", + out); + SET_SIZESTATDESC(1136, "responses sent 1136-1151 bytes", "1136-1151", + out); + SET_SIZESTATDESC(1152, "responses sent 1152-1167 bytes", "1152-1167", + out); + SET_SIZESTATDESC(1168, "responses sent 1168-1183 bytes", "1168-1183", + out); + SET_SIZESTATDESC(1184, "responses sent 1184-1199 bytes", "1184-1199", + out); + SET_SIZESTATDESC(1200, "responses sent 1200-1215 bytes", "1200-1215", + out); + SET_SIZESTATDESC(1216, "responses sent 1216-1231 bytes", "1216-1231", + out); + SET_SIZESTATDESC(1232, "responses sent 1232-1247 bytes", "1232-1247", + out); + SET_SIZESTATDESC(1248, "responses sent 1248-1263 bytes", "1248-1263", + out); + SET_SIZESTATDESC(1264, "responses sent 1264-1279 bytes", "1264-1279", + out); + SET_SIZESTATDESC(1280, "responses sent 1280-1295 bytes", "1280-1295", + out); + SET_SIZESTATDESC(1296, "responses sent 1296-1311 bytes", "1296-1311", + out); + SET_SIZESTATDESC(1312, "responses sent 1312-1327 bytes", "1312-1327", + out); + SET_SIZESTATDESC(1328, "responses sent 1328-1343 bytes", "1328-1343", + out); + SET_SIZESTATDESC(1344, "responses sent 1344-1359 bytes", "1344-1359", + out); + SET_SIZESTATDESC(1360, "responses sent 1360-1375 bytes", "1360-1375", + out); + SET_SIZESTATDESC(1376, "responses sent 1376-1391 bytes", "1376-1391", + out); + SET_SIZESTATDESC(1392, "responses sent 1392-1407 bytes", "1392-1407", + out); + SET_SIZESTATDESC(1408, "responses sent 1408-1423 bytes", "1408-1423", + out); + SET_SIZESTATDESC(1424, "responses sent 1424-1439 bytes", "1424-1439", + out); + SET_SIZESTATDESC(1440, "responses sent 1440-1455 bytes", "1440-1455", + out); + SET_SIZESTATDESC(1456, "responses sent 1456-1471 bytes", "1456-1471", + out); + SET_SIZESTATDESC(1472, "responses sent 1472-1487 bytes", "1472-1487", + out); + SET_SIZESTATDESC(1488, "responses sent 1488-1503 bytes", "1488-1503", + out); + SET_SIZESTATDESC(1504, "responses sent 1504-1519 bytes", "1504-1519", + out); + SET_SIZESTATDESC(1520, "responses sent 1520-1535 bytes", "1520-1535", + out); + SET_SIZESTATDESC(1536, "responses sent 1536-1551 bytes", "1536-1551", + out); + SET_SIZESTATDESC(1552, "responses sent 1552-1567 bytes", "1552-1567", + out); + SET_SIZESTATDESC(1568, "responses sent 1568-1583 bytes", "1568-1583", + out); + SET_SIZESTATDESC(1584, "responses sent 1584-1599 bytes", "1584-1599", + out); + SET_SIZESTATDESC(1600, "responses sent 1600-1615 bytes", "1600-1615", + out); + SET_SIZESTATDESC(1616, "responses sent 1616-1631 bytes", "1616-1631", + out); + SET_SIZESTATDESC(1632, "responses sent 1632-1647 bytes", "1632-1647", + out); + SET_SIZESTATDESC(1648, "responses sent 1648-1663 bytes", "1648-1663", + out); + SET_SIZESTATDESC(1664, "responses sent 1664-1679 bytes", "1664-1679", + out); + SET_SIZESTATDESC(1680, "responses sent 1680-1695 bytes", "1680-1695", + out); + SET_SIZESTATDESC(1696, "responses sent 1696-1711 bytes", "1696-1711", + out); + SET_SIZESTATDESC(1712, "responses sent 1712-1727 bytes", "1712-1727", + out); + SET_SIZESTATDESC(1728, "responses sent 1728-1743 bytes", "1728-1743", + out); + SET_SIZESTATDESC(1744, "responses sent 1744-1759 bytes", "1744-1759", + out); + SET_SIZESTATDESC(1760, "responses sent 1760-1775 bytes", "1760-1775", + out); + SET_SIZESTATDESC(1776, "responses sent 1776-1791 bytes", "1776-1791", + out); + SET_SIZESTATDESC(1792, "responses sent 1792-1807 bytes", "1792-1807", + out); + SET_SIZESTATDESC(1808, "responses sent 1808-1823 bytes", "1808-1823", + out); + SET_SIZESTATDESC(1824, "responses sent 1824-1839 bytes", "1824-1839", + out); + SET_SIZESTATDESC(1840, "responses sent 1840-1855 bytes", "1840-1855", + out); + SET_SIZESTATDESC(1856, "responses sent 1856-1871 bytes", "1856-1871", + out); + SET_SIZESTATDESC(1872, "responses sent 1872-1887 bytes", "1872-1887", + out); + SET_SIZESTATDESC(1888, "responses sent 1888-1903 bytes", "1888-1903", + out); + SET_SIZESTATDESC(1904, "responses sent 1904-1919 bytes", "1904-1919", + out); + SET_SIZESTATDESC(1920, "responses sent 1920-1935 bytes", "1920-1935", + out); + SET_SIZESTATDESC(1936, "responses sent 1936-1951 bytes", "1936-1951", + out); + SET_SIZESTATDESC(1952, "responses sent 1952-1967 bytes", "1952-1967", + out); + SET_SIZESTATDESC(1968, "responses sent 1968-1983 bytes", "1968-1983", + out); + SET_SIZESTATDESC(1984, "responses sent 1984-1999 bytes", "1984-1999", + out); + SET_SIZESTATDESC(2000, "responses sent 2000-2015 bytes", "2000-2015", + out); + SET_SIZESTATDESC(2016, "responses sent 2016-2031 bytes", "2016-2031", + out); + SET_SIZESTATDESC(2032, "responses sent 2032-2047 bytes", "2032-2047", + out); + SET_SIZESTATDESC(2048, "responses sent 2048-2063 bytes", "2048-2063", + out); + SET_SIZESTATDESC(2064, "responses sent 2064-2079 bytes", "2064-2079", + out); + SET_SIZESTATDESC(2080, "responses sent 2080-2095 bytes", "2080-2095", + out); + SET_SIZESTATDESC(2096, "responses sent 2096-2111 bytes", "2096-2111", + out); + SET_SIZESTATDESC(2112, "responses sent 2112-2127 bytes", "2112-2127", + out); + SET_SIZESTATDESC(2128, "responses sent 2128-2143 bytes", "2128-2143", + out); + SET_SIZESTATDESC(2144, "responses sent 2144-2159 bytes", "2144-2159", + out); + SET_SIZESTATDESC(2160, "responses sent 2160-2175 bytes", "2160-2175", + out); + SET_SIZESTATDESC(2176, "responses sent 2176-2191 bytes", "2176-2191", + out); + SET_SIZESTATDESC(2192, "responses sent 2192-2207 bytes", "2192-2207", + out); + SET_SIZESTATDESC(2208, "responses sent 2208-2223 bytes", "2208-2223", + out); + SET_SIZESTATDESC(2224, "responses sent 2224-2239 bytes", "2224-2239", + out); + SET_SIZESTATDESC(2240, "responses sent 2240-2255 bytes", "2240-2255", + out); + SET_SIZESTATDESC(2256, "responses sent 2256-2271 bytes", "2256-2271", + out); + SET_SIZESTATDESC(2272, "responses sent 2272-2287 bytes", "2272-2287", + out); + SET_SIZESTATDESC(2288, "responses sent 2288-2303 bytes", "2288-2303", + out); + SET_SIZESTATDESC(2304, "responses sent 2304-2319 bytes", "2304-2319", + out); + SET_SIZESTATDESC(2320, "responses sent 2320-2335 bytes", "2320-2335", + out); + SET_SIZESTATDESC(2336, "responses sent 2336-2351 bytes", "2336-2351", + out); + SET_SIZESTATDESC(2352, "responses sent 2352-2367 bytes", "2352-2367", + out); + SET_SIZESTATDESC(2368, "responses sent 2368-2383 bytes", "2368-2383", + out); + SET_SIZESTATDESC(2384, "responses sent 2384-2399 bytes", "2384-2399", + out); + SET_SIZESTATDESC(2400, "responses sent 2400-2415 bytes", "2400-2415", + out); + SET_SIZESTATDESC(2416, "responses sent 2416-2431 bytes", "2416-2431", + out); + SET_SIZESTATDESC(2432, "responses sent 2432-2447 bytes", "2432-2447", + out); + SET_SIZESTATDESC(2448, "responses sent 2448-2463 bytes", "2448-2463", + out); + SET_SIZESTATDESC(2464, "responses sent 2464-2479 bytes", "2464-2479", + out); + SET_SIZESTATDESC(2480, "responses sent 2480-2495 bytes", "2480-2495", + out); + SET_SIZESTATDESC(2496, "responses sent 2496-2511 bytes", "2496-2511", + out); + SET_SIZESTATDESC(2512, "responses sent 2512-2527 bytes", "2512-2527", + out); + SET_SIZESTATDESC(2528, "responses sent 2528-2543 bytes", "2528-2543", + out); + SET_SIZESTATDESC(2544, "responses sent 2544-2559 bytes", "2544-2559", + out); + SET_SIZESTATDESC(2560, "responses sent 2560-2575 bytes", "2560-2575", + out); + SET_SIZESTATDESC(2576, "responses sent 2576-2591 bytes", "2576-2591", + out); + SET_SIZESTATDESC(2592, "responses sent 2592-2607 bytes", "2592-2607", + out); + SET_SIZESTATDESC(2608, "responses sent 2608-2623 bytes", "2608-2623", + out); + SET_SIZESTATDESC(2624, "responses sent 2624-2639 bytes", "2624-2639", + out); + SET_SIZESTATDESC(2640, "responses sent 2640-2655 bytes", "2640-2655", + out); + SET_SIZESTATDESC(2656, "responses sent 2656-2671 bytes", "2656-2671", + out); + SET_SIZESTATDESC(2672, "responses sent 2672-2687 bytes", "2672-2687", + out); + SET_SIZESTATDESC(2688, "responses sent 2688-2703 bytes", "2688-2703", + out); + SET_SIZESTATDESC(2704, "responses sent 2704-2719 bytes", "2704-2719", + out); + SET_SIZESTATDESC(2720, "responses sent 2720-2735 bytes", "2720-2735", + out); + SET_SIZESTATDESC(2736, "responses sent 2736-2751 bytes", "2736-2751", + out); + SET_SIZESTATDESC(2752, "responses sent 2752-2767 bytes", "2752-2767", + out); + SET_SIZESTATDESC(2768, "responses sent 2768-2783 bytes", "2768-2783", + out); + SET_SIZESTATDESC(2784, "responses sent 2784-2799 bytes", "2784-2799", + out); + SET_SIZESTATDESC(2800, "responses sent 2800-2815 bytes", "2800-2815", + out); + SET_SIZESTATDESC(2816, "responses sent 2816-2831 bytes", "2816-2831", + out); + SET_SIZESTATDESC(2832, "responses sent 2832-2847 bytes", "2832-2847", + out); + SET_SIZESTATDESC(2848, "responses sent 2848-2863 bytes", "2848-2863", + out); + SET_SIZESTATDESC(2864, "responses sent 2864-2879 bytes", "2864-2879", + out); + SET_SIZESTATDESC(2880, "responses sent 2880-2895 bytes", "2880-2895", + out); + SET_SIZESTATDESC(2896, "responses sent 2896-2911 bytes", "2896-2911", + out); + SET_SIZESTATDESC(2912, "responses sent 2912-2927 bytes", "2912-2927", + out); + SET_SIZESTATDESC(2928, "responses sent 2928-2943 bytes", "2928-2943", + out); + SET_SIZESTATDESC(2944, "responses sent 2944-2959 bytes", "2944-2959", + out); + SET_SIZESTATDESC(2960, "responses sent 2960-2975 bytes", "2960-2975", + out); + SET_SIZESTATDESC(2976, "responses sent 2976-2991 bytes", "2976-2991", + out); + SET_SIZESTATDESC(2992, "responses sent 2992-3007 bytes", "2992-3007", + out); + SET_SIZESTATDESC(3008, "responses sent 3008-3023 bytes", "3008-3023", + out); + SET_SIZESTATDESC(3024, "responses sent 3024-3039 bytes", "3024-3039", + out); + SET_SIZESTATDESC(3040, "responses sent 3040-3055 bytes", "3040-3055", + out); + SET_SIZESTATDESC(3056, "responses sent 3056-3071 bytes", "3056-3071", + out); + SET_SIZESTATDESC(3072, "responses sent 3072-3087 bytes", "3072-3087", + out); + SET_SIZESTATDESC(3088, "responses sent 3088-3103 bytes", "3088-3103", + out); + SET_SIZESTATDESC(3104, "responses sent 3104-3119 bytes", "3104-3119", + out); + SET_SIZESTATDESC(3120, "responses sent 3120-3135 bytes", "3120-3135", + out); + SET_SIZESTATDESC(3136, "responses sent 3136-3151 bytes", "3136-3151", + out); + SET_SIZESTATDESC(3152, "responses sent 3152-3167 bytes", "3152-3167", + out); + SET_SIZESTATDESC(3168, "responses sent 3168-3183 bytes", "3168-3183", + out); + SET_SIZESTATDESC(3184, "responses sent 3184-3199 bytes", "3184-3199", + out); + SET_SIZESTATDESC(3200, "responses sent 3200-3215 bytes", "3200-3215", + out); + SET_SIZESTATDESC(3216, "responses sent 3216-3231 bytes", "3216-3231", + out); + SET_SIZESTATDESC(3232, "responses sent 3232-3247 bytes", "3232-3247", + out); + SET_SIZESTATDESC(3248, "responses sent 3248-3263 bytes", "3248-3263", + out); + SET_SIZESTATDESC(3264, "responses sent 3264-3279 bytes", "3264-3279", + out); + SET_SIZESTATDESC(3280, "responses sent 3280-3295 bytes", "3280-3295", + out); + SET_SIZESTATDESC(3296, "responses sent 3296-3311 bytes", "3296-3311", + out); + SET_SIZESTATDESC(3312, "responses sent 3312-3327 bytes", "3312-3327", + out); + SET_SIZESTATDESC(3328, "responses sent 3328-3343 bytes", "3328-3343", + out); + SET_SIZESTATDESC(3344, "responses sent 3344-3359 bytes", "3344-3359", + out); + SET_SIZESTATDESC(3360, "responses sent 3360-3375 bytes", "3360-3375", + out); + SET_SIZESTATDESC(3376, "responses sent 3376-3391 bytes", "3376-3391", + out); + SET_SIZESTATDESC(3392, "responses sent 3392-3407 bytes", "3392-3407", + out); + SET_SIZESTATDESC(3408, "responses sent 3408-3423 bytes", "3408-3423", + out); + SET_SIZESTATDESC(3424, "responses sent 3424-3439 bytes", "3424-3439", + out); + SET_SIZESTATDESC(3440, "responses sent 3440-3455 bytes", "3440-3455", + out); + SET_SIZESTATDESC(3456, "responses sent 3456-3471 bytes", "3456-3471", + out); + SET_SIZESTATDESC(3472, "responses sent 3472-3487 bytes", "3472-3487", + out); + SET_SIZESTATDESC(3488, "responses sent 3488-3503 bytes", "3488-3503", + out); + SET_SIZESTATDESC(3504, "responses sent 3504-3519 bytes", "3504-3519", + out); + SET_SIZESTATDESC(3520, "responses sent 3520-3535 bytes", "3520-3535", + out); + SET_SIZESTATDESC(3536, "responses sent 3536-3551 bytes", "3536-3551", + out); + SET_SIZESTATDESC(3552, "responses sent 3552-3567 bytes", "3552-3567", + out); + SET_SIZESTATDESC(3568, "responses sent 3568-3583 bytes", "3568-3583", + out); + SET_SIZESTATDESC(3584, "responses sent 3584-3599 bytes", "3584-3599", + out); + SET_SIZESTATDESC(3600, "responses sent 3600-3615 bytes", "3600-3615", + out); + SET_SIZESTATDESC(3616, "responses sent 3616-3631 bytes", "3616-3631", + out); + SET_SIZESTATDESC(3632, "responses sent 3632-3647 bytes", "3632-3647", + out); + SET_SIZESTATDESC(3648, "responses sent 3648-3663 bytes", "3648-3663", + out); + SET_SIZESTATDESC(3664, "responses sent 3664-3679 bytes", "3664-3679", + out); + SET_SIZESTATDESC(3680, "responses sent 3680-3695 bytes", "3680-3695", + out); + SET_SIZESTATDESC(3696, "responses sent 3696-3711 bytes", "3696-3711", + out); + SET_SIZESTATDESC(3712, "responses sent 3712-3727 bytes", "3712-3727", + out); + SET_SIZESTATDESC(3728, "responses sent 3728-3743 bytes", "3728-3743", + out); + SET_SIZESTATDESC(3744, "responses sent 3744-3759 bytes", "3744-3759", + out); + SET_SIZESTATDESC(3760, "responses sent 3760-3775 bytes", "3760-3775", + out); + SET_SIZESTATDESC(3776, "responses sent 3776-3791 bytes", "3776-3791", + out); + SET_SIZESTATDESC(3792, "responses sent 3792-3807 bytes", "3792-3807", + out); + SET_SIZESTATDESC(3808, "responses sent 3808-3823 bytes", "3808-3823", + out); + SET_SIZESTATDESC(3824, "responses sent 3824-3839 bytes", "3824-3839", + out); + SET_SIZESTATDESC(3840, "responses sent 3840-3855 bytes", "3840-3855", + out); + SET_SIZESTATDESC(3856, "responses sent 3856-3871 bytes", "3856-3871", + out); + SET_SIZESTATDESC(3872, "responses sent 3872-3887 bytes", "3872-3887", + out); + SET_SIZESTATDESC(3888, "responses sent 3888-3903 bytes", "3888-3903", + out); + SET_SIZESTATDESC(3904, "responses sent 3904-3919 bytes", "3904-3919", + out); + SET_SIZESTATDESC(3920, "responses sent 3920-3935 bytes", "3920-3935", + out); + SET_SIZESTATDESC(3936, "responses sent 3936-3951 bytes", "3936-3951", + out); + SET_SIZESTATDESC(3952, "responses sent 3952-3967 bytes", "3952-3967", + out); + SET_SIZESTATDESC(3968, "responses sent 3968-3983 bytes", "3968-3983", + out); + SET_SIZESTATDESC(3984, "responses sent 3984-3999 bytes", "3984-3999", + out); + SET_SIZESTATDESC(4000, "responses sent 4000-4015 bytes", "4000-4015", + out); + SET_SIZESTATDESC(4016, "responses sent 4016-4031 bytes", "4016-4031", + out); + SET_SIZESTATDESC(4032, "responses sent 4032-4047 bytes", "4032-4047", + out); + SET_SIZESTATDESC(4048, "responses sent 4048-4063 bytes", "4048-4063", + out); + SET_SIZESTATDESC(4064, "responses sent 4064-4079 bytes", "4064-4079", + out); + SET_SIZESTATDESC(4080, "responses sent 4080-4095 bytes", "4080-4095", + out); + SET_SIZESTATDESC(4096, "responses sent 4096+ bytes", "4096+", out); + INSIST(i == dns_sizecounter_out_max); + + /* Sanity check */ + for (i = 0; i < ns_statscounter_max; i++) { + INSIST(nsstats_desc[i] != NULL); + } + for (i = 0; i < dns_resstatscounter_max; i++) { + INSIST(resstats_desc[i] != NULL); + } + for (i = 0; i < dns_adbstats_max; i++) { + INSIST(adbstats_desc[i] != NULL); + } + for (i = 0; i < dns_zonestatscounter_max; i++) { + INSIST(zonestats_desc[i] != NULL); + } + for (i = 0; i < isc_sockstatscounter_max; i++) { + INSIST(sockstats_desc[i] != NULL); + } + for (i = 0; i < dns_dnssecstats_max; i++) { + INSIST(dnssecstats_desc[i] != NULL); + } + for (i = 0; i < dns_sizecounter_in_max; i++) { + INSIST(udpinsizestats_desc[i] != NULL); + INSIST(tcpinsizestats_desc[i] != NULL); + } + for (i = 0; i < dns_sizecounter_out_max; i++) { + INSIST(udpoutsizestats_desc[i] != NULL); + INSIST(tcpoutsizestats_desc[i] != NULL); + } +#if defined(EXTENDED_STATS) + for (i = 0; i < ns_statscounter_max; i++) { + INSIST(nsstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_resstatscounter_max; i++) { + INSIST(resstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_adbstats_max; i++) { + INSIST(adbstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_zonestatscounter_max; i++) { + INSIST(zonestats_xmldesc[i] != NULL); + } + for (i = 0; i < isc_sockstatscounter_max; i++) { + INSIST(sockstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_dnssecstats_max; i++) { + INSIST(dnssecstats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_sizecounter_in_max; i++) { + INSIST(udpinsizestats_xmldesc[i] != NULL); + INSIST(tcpinsizestats_xmldesc[i] != NULL); + } + for (i = 0; i < dns_sizecounter_out_max; i++) { + INSIST(udpoutsizestats_xmldesc[i] != NULL); + INSIST(tcpoutsizestats_xmldesc[i] != NULL); + } +#endif /* if defined(EXTENDED_STATS) */ +} + +/*% + * Dump callback functions. + */ +static void +generalstat_dump(isc_statscounter_t counter, uint64_t val, void *arg) { + stats_dumparg_t *dumparg = arg; + + REQUIRE(counter < dumparg->ncounters); + dumparg->countervalues[counter] = val; +} + +static isc_result_t +dump_counters(isc_stats_t *stats, isc_statsformat_t type, void *arg, + const char *category, const char **desc, int ncounters, + int *indices, uint64_t *values, int options) { + int i, idx; + uint64_t value; + stats_dumparg_t dumparg; + FILE *fp; +#ifdef HAVE_LIBXML2 + void *writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *job, *cat, *counter; +#endif /* ifdef HAVE_JSON_C */ + +#if !defined(EXTENDED_STATS) + UNUSED(category); +#endif /* if !defined(EXTENDED_STATS) */ + + dumparg.type = type; + dumparg.ncounters = ncounters; + dumparg.counterindices = indices; + dumparg.countervalues = values; + + memset(values, 0, sizeof(values[0]) * ncounters); + isc_stats_dump(stats, generalstat_dump, &dumparg, options); + +#ifdef HAVE_JSON_C + cat = job = (json_object *)arg; + if (ncounters > 0 && type == isc_statsformat_json) { + if (category != NULL) { + cat = json_object_new_object(); + if (cat == NULL) { + return (ISC_R_NOMEMORY); + } + json_object_object_add(job, category, cat); + } + } +#endif /* ifdef HAVE_JSON_C */ + + for (i = 0; i < ncounters; i++) { + idx = indices[i]; + value = values[idx]; + + if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0) { + continue; + } + + switch (dumparg.type) { + case isc_statsformat_file: + fp = arg; + fprintf(fp, "%20" PRIu64 " %s\n", value, desc[idx]); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = arg; + + if (category != NULL) { + /* */ + TRY0(xmlTextWriterStartElement( + writer, ISC_XMLCHAR category)); + + /* inside category */ + TRY0(xmlTextWriterStartElement( + writer, ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteString( + writer, ISC_XMLCHAR desc[idx])); + TRY0(xmlTextWriterEndElement(writer)); + /* */ + + /* */ + TRY0(xmlTextWriterStartElement( + writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteFormatString( + writer, "%" PRIu64, value)); + + TRY0(xmlTextWriterEndElement(writer)); + /* */ + TRY0(xmlTextWriterEndElement(writer)); + /* */ + } else { + TRY0(xmlTextWriterStartElement( + writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteAttribute( + writer, ISC_XMLCHAR "name", + ISC_XMLCHAR desc[idx])); + TRY0(xmlTextWriterWriteFormatString( + writer, "%" PRIu64, value)); + TRY0(xmlTextWriterEndElement(writer)); + /* counter */ + } + +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + counter = json_object_new_int64(value); + if (counter == NULL) { + return (ISC_R_NOMEMORY); + } + json_object_object_add(cat, desc[idx], counter); +#endif /* ifdef HAVE_JSON_C */ + break; + } + } + return (ISC_R_SUCCESS); +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at dump_counters()"); + return (ISC_R_FAILURE); +#endif /* ifdef HAVE_LIBXML2 */ +} + +static void +rdtypestat_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) { + char typebuf[64]; + const char *typestr; + stats_dumparg_t *dumparg = arg; + FILE *fp; +#ifdef HAVE_LIBXML2 + void *writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *zoneobj, *obj; +#endif /* ifdef HAVE_JSON_C */ + + if ((DNS_RDATASTATSTYPE_ATTR(type) & + DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) == 0) + { + dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf, + sizeof(typebuf)); + typestr = typebuf; + } else { + typestr = "Others"; + } + + switch (dumparg->type) { + case isc_statsformat_file: + fp = dumparg->arg; + fprintf(fp, "%20" PRIu64 " %s\n", val, typestr); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = dumparg->arg; + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR typestr)); + + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val)); + + TRY0(xmlTextWriterEndElement(writer)); /* type */ +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + zoneobj = (json_object *)dumparg->arg; + obj = json_object_new_int64(val); + if (obj == NULL) { + return; + } + json_object_object_add(zoneobj, typestr, obj); +#endif /* ifdef HAVE_JSON_C */ + break; + } + return; +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at rdtypestat_dump()"); + dumparg->result = ISC_R_FAILURE; + return; +#endif /* ifdef HAVE_LIBXML2 */ +} + +static bool +rdatastatstype_attr(dns_rdatastatstype_t type, unsigned int attr) { + return ((DNS_RDATASTATSTYPE_ATTR(type) & attr) != 0); +} + +static void +rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) { + stats_dumparg_t *dumparg = arg; + FILE *fp; + char typebuf[64]; + const char *typestr; + bool nxrrset = false; + bool stale = false; + bool ancient = false; +#ifdef HAVE_LIBXML2 + void *writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *zoneobj, *obj; + char buf[1024]; +#endif /* ifdef HAVE_JSON_C */ + + if ((DNS_RDATASTATSTYPE_ATTR(type) & + DNS_RDATASTATSTYPE_ATTR_NXDOMAIN) != 0) + { + typestr = "NXDOMAIN"; + } else if ((DNS_RDATASTATSTYPE_ATTR(type) & + DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0) + { + typestr = "Others"; + } else { + dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf, + sizeof(typebuf)); + typestr = typebuf; + } + + nxrrset = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_NXRRSET); + stale = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_STALE); + ancient = rdatastatstype_attr(type, DNS_RDATASTATSTYPE_ATTR_ANCIENT); + + switch (dumparg->type) { + case isc_statsformat_file: + fp = dumparg->arg; + fprintf(fp, "%20" PRIu64 " %s%s%s%s\n", val, ancient ? "~" : "", + stale ? "#" : "", nxrrset ? "!" : "", typestr); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = dumparg->arg; + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteFormatString( + writer, "%s%s%s%s", ancient ? "~" : "", + stale ? "#" : "", nxrrset ? "!" : "", typestr)); + TRY0(xmlTextWriterEndElement(writer)); /* name */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val)); + TRY0(xmlTextWriterEndElement(writer)); /* counter */ + + TRY0(xmlTextWriterEndElement(writer)); /* rrset */ +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + zoneobj = (json_object *)dumparg->arg; + snprintf(buf, sizeof(buf), "%s%s%s%s", ancient ? "~" : "", + stale ? "#" : "", nxrrset ? "!" : "", typestr); + obj = json_object_new_int64(val); + if (obj == NULL) { + return; + } + json_object_object_add(zoneobj, buf, obj); +#endif /* ifdef HAVE_JSON_C */ + break; + } + return; +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at rdatasetstats_dump()"); + dumparg->result = ISC_R_FAILURE; +#endif /* ifdef HAVE_LIBXML2 */ +} + +static void +opcodestat_dump(dns_opcode_t code, uint64_t val, void *arg) { + FILE *fp; + isc_buffer_t b; + char codebuf[64]; + stats_dumparg_t *dumparg = arg; +#ifdef HAVE_LIBXML2 + void *writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *zoneobj, *obj; +#endif /* ifdef HAVE_JSON_C */ + + isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1); + dns_opcode_totext(code, &b); + codebuf[isc_buffer_usedlength(&b)] = '\0'; + + switch (dumparg->type) { + case isc_statsformat_file: + fp = dumparg->arg; + fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = dumparg->arg; + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR codebuf)); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val)); + TRY0(xmlTextWriterEndElement(writer)); /* counter */ +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + zoneobj = (json_object *)dumparg->arg; + obj = json_object_new_int64(val); + if (obj == NULL) { + return; + } + json_object_object_add(zoneobj, codebuf, obj); +#endif /* ifdef HAVE_JSON_C */ + break; + } + return; + +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at opcodestat_dump()"); + dumparg->result = ISC_R_FAILURE; + return; +#endif /* ifdef HAVE_LIBXML2 */ +} + +static void +rcodestat_dump(dns_rcode_t code, uint64_t val, void *arg) { + FILE *fp; + isc_buffer_t b; + char codebuf[64]; + stats_dumparg_t *dumparg = arg; +#ifdef HAVE_LIBXML2 + void *writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *zoneobj, *obj; +#endif /* ifdef HAVE_JSON_C */ + + isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1); + dns_rcode_totext(code, &b); + codebuf[isc_buffer_usedlength(&b)] = '\0'; + + switch (dumparg->type) { + case isc_statsformat_file: + fp = dumparg->arg; + fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = dumparg->arg; + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR codebuf)); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val)); + TRY0(xmlTextWriterEndElement(writer)); /* counter */ +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + zoneobj = (json_object *)dumparg->arg; + obj = json_object_new_int64(val); + if (obj == NULL) { + return; + } + json_object_object_add(zoneobj, codebuf, obj); +#endif /* ifdef HAVE_JSON_C */ + break; + } + return; + +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at rcodestat_dump()"); + dumparg->result = ISC_R_FAILURE; + return; +#endif /* ifdef HAVE_LIBXML2 */ +} + +#if defined(EXTENDED_STATS) +static void +dnssecsignstat_dump(dns_keytag_t tag, uint64_t val, void *arg) { + FILE *fp; + char tagbuf[64]; + stats_dumparg_t *dumparg = arg; +#ifdef HAVE_LIBXML2 + xmlTextWriterPtr writer; + int xmlrc; +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + json_object *zoneobj, *obj; +#endif /* ifdef HAVE_JSON_C */ + + snprintf(tagbuf, sizeof(tagbuf), "%u", tag); + + switch (dumparg->type) { + case isc_statsformat_file: + fp = dumparg->arg; + fprintf(fp, "%20" PRIu64 " %s\n", val, tagbuf); + break; + case isc_statsformat_xml: +#ifdef HAVE_LIBXML2 + writer = dumparg->arg; + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR tagbuf)); + TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, val)); + TRY0(xmlTextWriterEndElement(writer)); /* counter */ +#endif /* ifdef HAVE_LIBXML2 */ + break; + case isc_statsformat_json: +#ifdef HAVE_JSON_C + zoneobj = (json_object *)dumparg->arg; + obj = json_object_new_int64(val); + if (obj == NULL) { + return; + } + json_object_object_add(zoneobj, tagbuf, obj); +#endif /* ifdef HAVE_JSON_C */ + break; + } + return; +#ifdef HAVE_LIBXML2 +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at dnssecsignstat_dump()"); + dumparg->result = ISC_R_FAILURE; + return; +#endif /* ifdef HAVE_LIBXML2 */ +} +#endif /* defined(EXTENDED_STATS) */ + +#ifdef HAVE_LIBXML2 +/* + * Which statistics to include when rendering to XML + */ +#define STATS_XML_STATUS 0x00 /* display only common statistics */ +#define STATS_XML_SERVER 0x01 +#define STATS_XML_ZONES 0x02 +#define STATS_XML_TASKS 0x04 +#define STATS_XML_NET 0x08 +#define STATS_XML_MEM 0x10 +#define STATS_XML_TRAFFIC 0x20 +#define STATS_XML_ALL 0xff + +static isc_result_t +zone_xmlrender(dns_zone_t *zone, void *arg) { + isc_result_t result; + char buf[1024 + 32]; /* sufficiently large for zone name and class */ + dns_rdataclass_t rdclass; + uint32_t serial; + xmlTextWriterPtr writer = arg; + dns_zonestat_level_t statlevel; + int xmlrc; + stats_dumparg_t dumparg; + const char *ztype; + isc_time_t timestamp; + + statlevel = dns_zone_getstatlevel(zone); + if (statlevel == dns_zonestat_none) { + return (ISC_R_SUCCESS); + } + + dumparg.type = isc_statsformat_xml; + dumparg.arg = writer; + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone")); + + dns_zone_nameonly(zone, buf, sizeof(buf)); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR buf)); + + rdclass = dns_zone_getclass(zone); + dns_rdataclass_format(rdclass, buf, sizeof(buf)); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "rdataclass", + ISC_XMLCHAR buf)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + ztype = user_zonetype(zone); + if (ztype != NULL) { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "unknown")); + } + TRY0(xmlTextWriterEndElement(writer)); /* type */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial")); + if (dns_zone_getserial(zone, &serial) == ISC_R_SUCCESS) { + TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial)); + } else { + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-")); + } + TRY0(xmlTextWriterEndElement(writer)); /* serial */ + + /* + * Export zone timers to the statistics channel in XML format. For + * primary zones, only include the loaded time. For secondary zones, + * also include the expire and refresh times. + */ + CHECK(dns_zone_getloadtime(zone, ×tamp)); + + isc_time_formatISO8601(×tamp, buf, 64); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "loaded")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf)); + TRY0(xmlTextWriterEndElement(writer)); + + if (dns_zone_gettype(zone) == dns_zone_secondary) { + CHECK(dns_zone_getexpiretime(zone, ×tamp)); + isc_time_formatISO8601(×tamp, buf, 64); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "expires")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf)); + TRY0(xmlTextWriterEndElement(writer)); + + CHECK(dns_zone_getrefreshtime(zone, ×tamp)); + isc_time_formatISO8601(×tamp, buf, 64); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "refresh")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR buf)); + TRY0(xmlTextWriterEndElement(writer)); + } + + if (statlevel == dns_zonestat_full) { + isc_stats_t *zonestats; + isc_stats_t *gluecachestats; + dns_stats_t *rcvquerystats; + dns_stats_t *dnssecsignstats; + uint64_t nsstat_values[ns_statscounter_max]; + uint64_t gluecachestats_values[dns_gluecachestatscounter_max]; + + zonestats = dns_zone_getrequeststats(zone); + if (zonestats != NULL) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, + ISC_XMLCHAR "type", + ISC_XMLCHAR "rcode")); + + CHECK(dump_counters(zonestats, isc_statsformat_xml, + writer, NULL, nsstats_xmldesc, + ns_statscounter_max, nsstats_index, + nsstat_values, + ISC_STATSDUMP_VERBOSE)); + /* counters type="rcode"*/ + TRY0(xmlTextWriterEndElement(writer)); + } + + gluecachestats = dns_zone_getgluecachestats(zone); + if (gluecachestats != NULL) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute( + writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "gluecache")); + + CHECK(dump_counters( + gluecachestats, isc_statsformat_xml, writer, + NULL, gluecachestats_xmldesc, + dns_gluecachestatscounter_max, + gluecachestats_index, gluecachestats_values, + ISC_STATSDUMP_VERBOSE)); + /* counters type="rcode"*/ + TRY0(xmlTextWriterEndElement(writer)); + } + + rcvquerystats = dns_zone_getrcvquerystats(zone); + if (rcvquerystats != NULL) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, + ISC_XMLCHAR "type", + ISC_XMLCHAR "qtype")); + + dumparg.result = ISC_R_SUCCESS; + dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump, + &dumparg, 0); + CHECK(dumparg.result); + + /* counters type="qtype"*/ + TRY0(xmlTextWriterEndElement(writer)); + } + + dnssecsignstats = dns_zone_getdnssecsignstats(zone); + if (dnssecsignstats != NULL) { + /* counters type="dnssec-sign"*/ + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute( + writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "dnssec-sign")); + + dumparg.result = ISC_R_SUCCESS; + dns_dnssecsignstats_dump( + dnssecsignstats, dns_dnssecsignstats_sign, + dnssecsignstat_dump, &dumparg, 0); + CHECK(dumparg.result); + + /* counters type="dnssec-sign"*/ + TRY0(xmlTextWriterEndElement(writer)); + + /* counters type="dnssec-refresh"*/ + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute( + writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "dnssec-refresh")); + + dumparg.result = ISC_R_SUCCESS; + dns_dnssecsignstats_dump( + dnssecsignstats, dns_dnssecsignstats_refresh, + dnssecsignstat_dump, &dumparg, 0); + CHECK(dumparg.result); + + /* counters type="dnssec-refresh"*/ + TRY0(xmlTextWriterEndElement(writer)); + } + } + + TRY0(xmlTextWriterEndElement(writer)); /* zone */ + + return (ISC_R_SUCCESS); +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "Failed at zone_xmlrender()"); + return (ISC_R_FAILURE); +} + +static isc_result_t +generatexml(named_server_t *server, uint32_t flags, int *buflen, + xmlChar **buf) { + char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + isc_time_t now; + xmlTextWriterPtr writer = NULL; + xmlDocPtr doc = NULL; + int xmlrc; + dns_view_t *view; + stats_dumparg_t dumparg; + dns_stats_t *cacherrstats; + uint64_t nsstat_values[ns_statscounter_max]; + uint64_t resstat_values[dns_resstatscounter_max]; + uint64_t adbstat_values[dns_adbstats_max]; + uint64_t zonestat_values[dns_zonestatscounter_max]; + uint64_t sockstat_values[isc_sockstatscounter_max]; + uint64_t udpinsizestat_values[dns_sizecounter_in_max]; + uint64_t udpoutsizestat_values[dns_sizecounter_out_max]; + uint64_t tcpinsizestat_values[dns_sizecounter_in_max]; + uint64_t tcpoutsizestat_values[dns_sizecounter_out_max]; +#ifdef HAVE_DNSTAP + uint64_t dnstapstat_values[dns_dnstapcounter_max]; +#endif /* ifdef HAVE_DNSTAP */ + isc_result_t result; + + isc_time_now(&now); + isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof boottime); + isc_time_formatISO8601ms(&named_g_configtime, configtime, + sizeof configtime); + isc_time_formatISO8601ms(&now, nowstr, sizeof nowstr); + + writer = xmlNewTextWriterDoc(&doc, 0); + if (writer == NULL) { + goto cleanup; + } + TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL)); + TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet", + ISC_XMLCHAR "type=\"text/xsl\" " + "href=\"/bind9.xsl\"")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version", + ISC_XMLCHAR STATS_XML_VERSION)); + + /* Set common fields for statistics dump */ + dumparg.type = isc_statsformat_xml; + dumparg.arg = writer; + + /* Render server information */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime)); + TRY0(xmlTextWriterEndElement(writer)); /* boot-time */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "config-time")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR configtime)); + TRY0(xmlTextWriterEndElement(writer)); /* config-time */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr)); + TRY0(xmlTextWriterEndElement(writer)); /* current-time */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "version")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR PACKAGE_VERSION)); + TRY0(xmlTextWriterEndElement(writer)); /* version */ + + if ((flags & STATS_XML_SERVER) != 0) { + dumparg.result = ISC_R_SUCCESS; + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "opcode")); + + dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump, + &dumparg, ISC_STATSDUMP_VERBOSE); + CHECK(dumparg.result); + + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "rcode")); + + dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump, + &dumparg, ISC_STATSDUMP_VERBOSE); + CHECK(dumparg.result); + + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "qtype")); + + dumparg.result = ISC_R_SUCCESS; + dns_rdatatypestats_dump(server->sctx->rcvquerystats, + rdtypestat_dump, &dumparg, 0); + CHECK(dumparg.result); + + TRY0(xmlTextWriterEndElement(writer)); /* counters */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "nsstat")); + + CHECK(dump_counters(ns_stats_get(server->sctx->nsstats), + isc_statsformat_xml, writer, NULL, + nsstats_xmldesc, ns_statscounter_max, + nsstats_index, nsstat_values, + ISC_STATSDUMP_VERBOSE)); + + TRY0(xmlTextWriterEndElement(writer)); /* /nsstat */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "zonestat")); + + CHECK(dump_counters(server->zonestats, isc_statsformat_xml, + writer, NULL, zonestats_xmldesc, + dns_zonestatscounter_max, zonestats_index, + zonestat_values, ISC_STATSDUMP_VERBOSE)); + + TRY0(xmlTextWriterEndElement(writer)); /* /zonestat */ + + /* + * Most of the common resolver statistics entries are 0, so + * we don't use the verbose dump here. + */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "resstat")); + CHECK(dump_counters(server->resolverstats, isc_statsformat_xml, + writer, NULL, resstats_xmldesc, + dns_resstatscounter_max, resstats_index, + resstat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* resstat */ + +#ifdef HAVE_DNSTAP + if (server->dtenv != NULL) { + isc_stats_t *dnstapstats = NULL; + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, + ISC_XMLCHAR "type", + ISC_XMLCHAR "dnstap")); + dns_dt_getstats(named_g_server->dtenv, &dnstapstats); + result = dump_counters( + dnstapstats, isc_statsformat_xml, writer, NULL, + dnstapstats_xmldesc, dns_dnstapcounter_max, + dnstapstats_index, dnstapstat_values, 0); + isc_stats_detach(&dnstapstats); + CHECK(result); + + TRY0(xmlTextWriterEndElement(writer)); /* dnstap */ + } +#endif /* ifdef HAVE_DNSTAP */ + } + + if ((flags & STATS_XML_NET) != 0) { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "sockstat")); + + CHECK(dump_counters(server->sockstats, isc_statsformat_xml, + writer, NULL, sockstats_xmldesc, + isc_sockstatscounter_max, sockstats_index, + sockstat_values, ISC_STATSDUMP_VERBOSE)); + + TRY0(xmlTextWriterEndElement(writer)); /* /sockstat */ + } + TRY0(xmlTextWriterEndElement(writer)); /* /server */ + + if ((flags & STATS_XML_TRAFFIC) != 0) { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "traffic")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv4")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "request-size")); + + CHECK(dump_counters( + server->sctx->udpinstats4, isc_statsformat_xml, writer, + NULL, udpinsizestats_xmldesc, dns_sizecounter_in_max, + udpinsizestats_index, udpinsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "response-size")); + + CHECK(dump_counters( + server->sctx->udpoutstats4, isc_statsformat_xml, writer, + NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max, + udpoutsizestats_index, udpoutsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "request-size")); + + CHECK(dump_counters( + server->sctx->tcpinstats4, isc_statsformat_xml, writer, + NULL, tcpinsizestats_xmldesc, dns_sizecounter_in_max, + tcpinsizestats_index, tcpinsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "response-size")); + + CHECK(dump_counters( + server->sctx->tcpoutstats4, isc_statsformat_xml, writer, + NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max, + tcpoutsizestats_index, tcpoutsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv6")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "request-size")); + + CHECK(dump_counters( + server->sctx->udpinstats6, isc_statsformat_xml, writer, + NULL, udpinsizestats_xmldesc, dns_sizecounter_in_max, + udpinsizestats_index, udpinsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "response-size")); + + CHECK(dump_counters( + server->sctx->udpoutstats6, isc_statsformat_xml, writer, + NULL, udpoutsizestats_xmldesc, dns_sizecounter_out_max, + udpoutsizestats_index, udpoutsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp")); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "request-size")); + + CHECK(dump_counters( + server->sctx->tcpinstats6, isc_statsformat_xml, writer, + NULL, tcpinsizestats_xmldesc, dns_sizecounter_in_max, + tcpinsizestats_index, tcpinsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "response-size")); + + CHECK(dump_counters( + server->sctx->tcpoutstats6, isc_statsformat_xml, writer, + NULL, tcpoutsizestats_xmldesc, dns_sizecounter_out_max, + tcpoutsizestats_index, tcpoutsizestat_values, 0)); + + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + TRY0(xmlTextWriterEndElement(writer)); /* */ + } + + /* + * Render views. For each view we know of, call its + * rendering function. + */ + view = ISC_LIST_HEAD(server->viewlist); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views")); + while (view != NULL && + ((flags & (STATS_XML_SERVER | STATS_XML_ZONES)) != 0)) + { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", + ISC_XMLCHAR view->name)); + + if ((flags & STATS_XML_ZONES) != 0) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "zones")); + CHECK(dns_zt_apply(view->zonetable, isc_rwlocktype_read, + true, NULL, zone_xmlrender, writer)); + TRY0(xmlTextWriterEndElement(writer)); /* /zones */ + } + + if ((flags & STATS_XML_SERVER) == 0) { + TRY0(xmlTextWriterEndElement(writer)); /* /view */ + view = ISC_LIST_NEXT(view, link); + continue; + } + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "resqtype")); + + if (view->resquerystats != NULL) { + dumparg.result = ISC_R_SUCCESS; + dns_rdatatypestats_dump(view->resquerystats, + rdtypestat_dump, &dumparg, 0); + CHECK(dumparg.result); + } + TRY0(xmlTextWriterEndElement(writer)); + + /* */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "resstats")); + if (view->resstats != NULL) { + CHECK(dump_counters(view->resstats, isc_statsformat_xml, + writer, NULL, resstats_xmldesc, + dns_resstatscounter_max, + resstats_index, resstat_values, + ISC_STATSDUMP_VERBOSE)); + } + TRY0(xmlTextWriterEndElement(writer)); /* */ + + cacherrstats = dns_db_getrrsetstats(view->cachedb); + if (cacherrstats != NULL) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "cache")); + TRY0(xmlTextWriterWriteAttribute( + writer, ISC_XMLCHAR "name", + ISC_XMLCHAR dns_cache_getname(view->cache))); + dumparg.result = ISC_R_SUCCESS; + dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump, + &dumparg, 0); + CHECK(dumparg.result); + TRY0(xmlTextWriterEndElement(writer)); /* cache */ + } + + /* */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "adbstat")); + if (view->adbstats != NULL) { + CHECK(dump_counters(view->adbstats, isc_statsformat_xml, + writer, NULL, adbstats_xmldesc, + dns_adbstats_max, adbstats_index, + adbstat_values, + ISC_STATSDUMP_VERBOSE)); + } + TRY0(xmlTextWriterEndElement(writer)); /* */ + + /* */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters")); + TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR "cachestats")); + TRY0(dns_cache_renderxml(view->cache, writer)); + TRY0(xmlTextWriterEndElement(writer)); /* */ + + TRY0(xmlTextWriterEndElement(writer)); /* view */ + + view = ISC_LIST_NEXT(view, link); + } + TRY0(xmlTextWriterEndElement(writer)); /* /views */ + + if ((flags & STATS_XML_TASKS) != 0) { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr")); + TRY0(isc_taskmgr_renderxml(named_g_taskmgr, writer)); + TRY0(xmlTextWriterEndElement(writer)); /* /taskmgr */ + } + + if ((flags & STATS_XML_MEM) != 0) { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory")); + TRY0(isc_mem_renderxml(writer)); + TRY0(xmlTextWriterEndElement(writer)); /* /memory */ + } + + TRY0(xmlTextWriterEndElement(writer)); /* /statistics */ + TRY0(xmlTextWriterEndDocument(writer)); + + xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 0); + if (*buf == NULL) { + goto cleanup; + } + + xmlFreeTextWriter(writer); + xmlFreeDoc(doc); + return (ISC_R_SUCCESS); + +cleanup: + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed generating XML response"); + if (writer != NULL) { + xmlFreeTextWriter(writer); + } + if (doc != NULL) { + xmlFreeDoc(doc); + } + return (ISC_R_FAILURE); +} + +static void +wrap_xmlfree(isc_buffer_t *buffer, void *arg) { + UNUSED(arg); + + xmlFree(isc_buffer_base(buffer)); +} + +static isc_result_t +render_xml(uint32_t flags, void *arg, unsigned int *retcode, + const char **retmsg, const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + unsigned char *msg = NULL; + int msglen; + named_server_t *server = arg; + isc_result_t result; + + result = generatexml(server, flags, &msglen, &msg); + + if (result == ISC_R_SUCCESS) { + *retcode = 200; + *retmsg = "OK"; + *mimetype = "text/xml"; + isc_buffer_reinit(b, msg, msglen); + isc_buffer_add(b, msglen); + *freecb = wrap_xmlfree; + *freecb_args = NULL; + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at rendering XML()"); + } + + return (result); +} + +static isc_result_t +render_xml_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb, + void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_ALL, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_STATUS, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_SERVER, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_ZONES, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb, + void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_NET, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_TASKS, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, isc_httpdfree_t **freecb, + void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_MEM, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_xml_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_xml(STATS_XML_TRAFFIC, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON_C +/* + * Which statistics to include when rendering to JSON + */ +#define STATS_JSON_STATUS 0x00 /* display only common statistics */ +#define STATS_JSON_SERVER 0x01 +#define STATS_JSON_ZONES 0x02 +#define STATS_JSON_TASKS 0x04 +#define STATS_JSON_NET 0x08 +#define STATS_JSON_MEM 0x10 +#define STATS_JSON_TRAFFIC 0x20 +#define STATS_JSON_ALL 0xff + +#define CHECKMEM(m) \ + do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY; \ + goto cleanup; \ + } \ + } while (0) + +static void +wrap_jsonfree(isc_buffer_t *buffer, void *arg) { + json_object_put(isc_buffer_base(buffer)); + if (arg != NULL) { + json_object_put((json_object *)arg); + } +} + +static json_object * +addzone(char *name, char *classname, const char *ztype, uint32_t serial, + bool add_serial) { + json_object *node = json_object_new_object(); + + if (node == NULL) { + return (NULL); + } + + json_object_object_add(node, "name", json_object_new_string(name)); + json_object_object_add(node, "class", + json_object_new_string(classname)); + if (add_serial) { + json_object_object_add(node, "serial", + json_object_new_int64(serial)); + } + if (ztype != NULL) { + json_object_object_add(node, "type", + json_object_new_string(ztype)); + } + return (node); +} + +static isc_result_t +zone_jsonrender(dns_zone_t *zone, void *arg) { + isc_result_t result = ISC_R_SUCCESS; + char buf[1024 + 32]; /* sufficiently large for zone name and class */ + char classbuf[64]; /* sufficiently large for class */ + char *zone_name_only = NULL; + char *class_only = NULL; + dns_rdataclass_t rdclass; + uint32_t serial; + json_object *zonearray = (json_object *)arg; + json_object *zoneobj = NULL; + dns_zonestat_level_t statlevel; + isc_time_t timestamp; + + statlevel = dns_zone_getstatlevel(zone); + if (statlevel == dns_zonestat_none) { + return (ISC_R_SUCCESS); + } + + dns_zone_nameonly(zone, buf, sizeof(buf)); + zone_name_only = buf; + + rdclass = dns_zone_getclass(zone); + dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf)); + class_only = classbuf; + + if (dns_zone_getserial(zone, &serial) != ISC_R_SUCCESS) { + zoneobj = addzone(zone_name_only, class_only, + user_zonetype(zone), 0, false); + } else { + zoneobj = addzone(zone_name_only, class_only, + user_zonetype(zone), serial, true); + } + + if (zoneobj == NULL) { + return (ISC_R_NOMEMORY); + } + + /* + * Export zone timers to the statistics channel in JSON format. + * For primary zones, only include the loaded time. For secondary + * zones, also include the expire and refresh times. + */ + + CHECK(dns_zone_getloadtime(zone, ×tamp)); + + isc_time_formatISO8601(×tamp, buf, 64); + json_object_object_add(zoneobj, "loaded", json_object_new_string(buf)); + + if (dns_zone_gettype(zone) == dns_zone_secondary) { + CHECK(dns_zone_getexpiretime(zone, ×tamp)); + isc_time_formatISO8601(×tamp, buf, 64); + json_object_object_add(zoneobj, "expires", + json_object_new_string(buf)); + + CHECK(dns_zone_getrefreshtime(zone, ×tamp)); + isc_time_formatISO8601(×tamp, buf, 64); + json_object_object_add(zoneobj, "refresh", + json_object_new_string(buf)); + } + + if (statlevel == dns_zonestat_full) { + isc_stats_t *zonestats; + isc_stats_t *gluecachestats; + dns_stats_t *rcvquerystats; + dns_stats_t *dnssecsignstats; + uint64_t nsstat_values[ns_statscounter_max]; + uint64_t gluecachestats_values[dns_gluecachestatscounter_max]; + + zonestats = dns_zone_getrequeststats(zone); + if (zonestats != NULL) { + json_object *counters = json_object_new_object(); + if (counters == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = dump_counters(zonestats, isc_statsformat_json, + counters, NULL, nsstats_xmldesc, + ns_statscounter_max, + nsstats_index, nsstat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(zoneobj, "rcodes", + counters); + } else { + json_object_put(counters); + } + } + + gluecachestats = dns_zone_getgluecachestats(zone); + if (gluecachestats != NULL) { + json_object *counters = json_object_new_object(); + if (counters == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + result = dump_counters( + gluecachestats, isc_statsformat_json, counters, + NULL, gluecachestats_xmldesc, + dns_gluecachestatscounter_max, + gluecachestats_index, gluecachestats_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(zoneobj, "gluecache", + counters); + } else { + json_object_put(counters); + } + } + + rcvquerystats = dns_zone_getrcvquerystats(zone); + if (rcvquerystats != NULL) { + stats_dumparg_t dumparg; + json_object *counters = json_object_new_object(); + CHECKMEM(counters); + + dumparg.type = isc_statsformat_json; + dumparg.arg = counters; + dumparg.result = ISC_R_SUCCESS; + dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump, + &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(zoneobj, "qtypes", + counters); + } else { + json_object_put(counters); + } + } + + dnssecsignstats = dns_zone_getdnssecsignstats(zone); + if (dnssecsignstats != NULL) { + stats_dumparg_t dumparg; + json_object *sign_counters = json_object_new_object(); + CHECKMEM(sign_counters); + + dumparg.type = isc_statsformat_json; + dumparg.arg = sign_counters; + dumparg.result = ISC_R_SUCCESS; + dns_dnssecsignstats_dump( + dnssecsignstats, dns_dnssecsignstats_sign, + dnssecsignstat_dump, &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(sign_counters); + goto cleanup; + } + + if (json_object_get_object(sign_counters)->count != 0) { + json_object_object_add(zoneobj, "dnssec-sign", + sign_counters); + } else { + json_object_put(sign_counters); + } + + json_object *refresh_counters = + json_object_new_object(); + CHECKMEM(refresh_counters); + + dumparg.type = isc_statsformat_json; + dumparg.arg = refresh_counters; + dumparg.result = ISC_R_SUCCESS; + dns_dnssecsignstats_dump( + dnssecsignstats, dns_dnssecsignstats_refresh, + dnssecsignstat_dump, &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(refresh_counters); + goto cleanup; + } + + if (json_object_get_object(refresh_counters)->count != + 0) + { + json_object_object_add(zoneobj, + "dnssec-refresh", + refresh_counters); + } else { + json_object_put(refresh_counters); + } + } + } + + json_object_array_add(zonearray, zoneobj); + zoneobj = NULL; + result = ISC_R_SUCCESS; + +cleanup: + if (zoneobj != NULL) { + json_object_put(zoneobj); + } + return (result); +} + +static isc_result_t +generatejson(named_server_t *server, size_t *msglen, const char **msg, + json_object **rootp, uint32_t flags) { + dns_view_t *view; + isc_result_t result = ISC_R_SUCCESS; + json_object *bindstats, *viewlist, *counters, *obj; + json_object *traffic = NULL; + json_object *udpreq4 = NULL, *udpresp4 = NULL; + json_object *tcpreq4 = NULL, *tcpresp4 = NULL; + json_object *udpreq6 = NULL, *udpresp6 = NULL; + json_object *tcpreq6 = NULL, *tcpresp6 = NULL; + uint64_t nsstat_values[ns_statscounter_max]; + uint64_t resstat_values[dns_resstatscounter_max]; + uint64_t adbstat_values[dns_adbstats_max]; + uint64_t zonestat_values[dns_zonestatscounter_max]; + uint64_t sockstat_values[isc_sockstatscounter_max]; + uint64_t udpinsizestat_values[dns_sizecounter_in_max]; + uint64_t udpoutsizestat_values[dns_sizecounter_out_max]; + uint64_t tcpinsizestat_values[dns_sizecounter_in_max]; + uint64_t tcpoutsizestat_values[dns_sizecounter_out_max]; +#ifdef HAVE_DNSTAP + uint64_t dnstapstat_values[dns_dnstapcounter_max]; +#endif /* ifdef HAVE_DNSTAP */ + stats_dumparg_t dumparg; + char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"]; + isc_time_t now; + + REQUIRE(msglen != NULL); + REQUIRE(msg != NULL && *msg == NULL); + REQUIRE(rootp == NULL || *rootp == NULL); + + bindstats = json_object_new_object(); + if (bindstats == NULL) { + return (ISC_R_NOMEMORY); + } + + /* + * These statistics are included no matter which URL we use. + */ + obj = json_object_new_string(STATS_JSON_VERSION); + CHECKMEM(obj); + json_object_object_add(bindstats, "json-stats-version", obj); + + isc_time_now(&now); + isc_time_formatISO8601ms(&named_g_boottime, boottime, sizeof(boottime)); + isc_time_formatISO8601ms(&named_g_configtime, configtime, + sizeof configtime); + isc_time_formatISO8601ms(&now, nowstr, sizeof(nowstr)); + + obj = json_object_new_string(boottime); + CHECKMEM(obj); + json_object_object_add(bindstats, "boot-time", obj); + + obj = json_object_new_string(configtime); + CHECKMEM(obj); + json_object_object_add(bindstats, "config-time", obj); + + obj = json_object_new_string(nowstr); + CHECKMEM(obj); + json_object_object_add(bindstats, "current-time", obj); + obj = json_object_new_string(PACKAGE_VERSION); + CHECKMEM(obj); + json_object_object_add(bindstats, "version", obj); + + if ((flags & STATS_JSON_SERVER) != 0) { + /* OPCODE counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.type = isc_statsformat_json; + dumparg.arg = counters; + + dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump, + &dumparg, ISC_STATSDUMP_VERBOSE); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "opcodes", counters); + } else { + json_object_put(counters); + } + + /* OPCODE counters */ + counters = json_object_new_object(); + + dumparg.type = isc_statsformat_json; + dumparg.arg = counters; + + dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump, + &dumparg, ISC_STATSDUMP_VERBOSE); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "rcodes", counters); + } else { + json_object_put(counters); + } + + /* QTYPE counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + + dns_rdatatypestats_dump(server->sctx->rcvquerystats, + rdtypestat_dump, &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "qtypes", counters); + } else { + json_object_put(counters); + } + + /* server stat counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + + result = dump_counters(ns_stats_get(server->sctx->nsstats), + isc_statsformat_json, counters, NULL, + nsstats_xmldesc, ns_statscounter_max, + nsstats_index, nsstat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "nsstats", counters); + } else { + json_object_put(counters); + } + + /* zone stat counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + + result = dump_counters(server->zonestats, isc_statsformat_json, + counters, NULL, zonestats_xmldesc, + dns_zonestatscounter_max, + zonestats_index, zonestat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "zonestats", + counters); + } else { + json_object_put(counters); + } + + /* resolver stat counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + + result = dump_counters( + server->resolverstats, isc_statsformat_json, counters, + NULL, resstats_xmldesc, dns_resstatscounter_max, + resstats_index, resstat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "resstats", counters); + } else { + json_object_put(counters); + } + +#ifdef HAVE_DNSTAP + /* dnstap stat counters */ + if (named_g_server->dtenv != NULL) { + isc_stats_t *dnstapstats = NULL; + dns_dt_getstats(named_g_server->dtenv, &dnstapstats); + counters = json_object_new_object(); + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + result = dump_counters( + dnstapstats, isc_statsformat_json, counters, + NULL, dnstapstats_xmldesc, + dns_dnstapcounter_max, dnstapstats_index, + dnstapstat_values, 0); + isc_stats_detach(&dnstapstats); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "dnstapstats", + counters); + } else { + json_object_put(counters); + } + } +#endif /* ifdef HAVE_DNSTAP */ + } + + if ((flags & (STATS_JSON_ZONES | STATS_JSON_SERVER)) != 0) { + viewlist = json_object_new_object(); + CHECKMEM(viewlist); + + json_object_object_add(bindstats, "views", viewlist); + + view = ISC_LIST_HEAD(server->viewlist); + while (view != NULL) { + json_object *za, *v = json_object_new_object(); + + CHECKMEM(v); + json_object_object_add(viewlist, view->name, v); + + za = json_object_new_array(); + CHECKMEM(za); + + if ((flags & STATS_JSON_ZONES) != 0) { + CHECK(dns_zt_apply(view->zonetable, + isc_rwlocktype_read, true, + NULL, zone_jsonrender, za)); + } + + if (json_object_array_length(za) != 0) { + json_object_object_add(v, "zones", za); + } else { + json_object_put(za); + } + + if ((flags & STATS_JSON_SERVER) != 0) { + json_object *res; + dns_stats_t *dstats; + isc_stats_t *istats; + + res = json_object_new_object(); + CHECKMEM(res); + json_object_object_add(v, "resolver", res); + + istats = view->resstats; + if (istats != NULL) { + counters = json_object_new_object(); + CHECKMEM(counters); + + result = dump_counters( + istats, isc_statsformat_json, + counters, NULL, + resstats_xmldesc, + dns_resstatscounter_max, + resstats_index, resstat_values, + 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + result = dumparg.result; + goto cleanup; + } + + json_object_object_add(res, "stats", + counters); + } + + dstats = view->resquerystats; + if (dstats != NULL) { + counters = json_object_new_object(); + CHECKMEM(counters); + + dumparg.arg = counters; + dumparg.result = ISC_R_SUCCESS; + dns_rdatatypestats_dump(dstats, + rdtypestat_dump, + &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + result = dumparg.result; + goto cleanup; + } + + json_object_object_add(res, "qtypes", + counters); + } + + dstats = dns_db_getrrsetstats(view->cachedb); + if (dstats != NULL) { + counters = json_object_new_object(); + CHECKMEM(counters); + + dumparg.arg = counters; + dumparg.result = ISC_R_SUCCESS; + dns_rdatasetstats_dump( + dstats, rdatasetstats_dump, + &dumparg, 0); + if (dumparg.result != ISC_R_SUCCESS) { + json_object_put(counters); + result = dumparg.result; + goto cleanup; + } + + json_object_object_add(res, "cache", + counters); + } + + counters = json_object_new_object(); + CHECKMEM(counters); + + result = dns_cache_renderjson(view->cache, + counters); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + json_object_object_add(res, "cachestats", + counters); + + istats = view->adbstats; + if (istats != NULL) { + counters = json_object_new_object(); + CHECKMEM(counters); + + result = dump_counters( + istats, isc_statsformat_json, + counters, NULL, + adbstats_xmldesc, + dns_adbstats_max, + adbstats_index, adbstat_values, + 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + result = dumparg.result; + goto cleanup; + } + + json_object_object_add(res, "adb", + counters); + } + } + + view = ISC_LIST_NEXT(view, link); + } + } + + if ((flags & STATS_JSON_NET) != 0) { + /* socket stat counters */ + counters = json_object_new_object(); + + dumparg.result = ISC_R_SUCCESS; + dumparg.arg = counters; + + result = dump_counters(server->sockstats, isc_statsformat_json, + counters, NULL, sockstats_xmldesc, + isc_sockstatscounter_max, + sockstats_index, sockstat_values, 0); + if (result != ISC_R_SUCCESS) { + json_object_put(counters); + goto cleanup; + } + + if (json_object_get_object(counters)->count != 0) { + json_object_object_add(bindstats, "sockstats", + counters); + } else { + json_object_put(counters); + } + } + + if ((flags & STATS_JSON_TASKS) != 0) { + json_object *tasks = json_object_new_object(); + CHECKMEM(tasks); + + result = isc_taskmgr_renderjson(named_g_taskmgr, tasks); + if (result != ISC_R_SUCCESS) { + json_object_put(tasks); + goto cleanup; + } + + json_object_object_add(bindstats, "taskmgr", tasks); + } + + if ((flags & STATS_JSON_MEM) != 0) { + json_object *memory = json_object_new_object(); + CHECKMEM(memory); + + result = isc_mem_renderjson(memory); + if (result != ISC_R_SUCCESS) { + json_object_put(memory); + goto cleanup; + } + + json_object_object_add(bindstats, "memory", memory); + } + + if ((flags & STATS_JSON_TRAFFIC) != 0) { + traffic = json_object_new_object(); + CHECKMEM(traffic); + + udpreq4 = json_object_new_object(); + CHECKMEM(udpreq4); + + udpresp4 = json_object_new_object(); + CHECKMEM(udpresp4); + + tcpreq4 = json_object_new_object(); + CHECKMEM(tcpreq4); + + tcpresp4 = json_object_new_object(); + CHECKMEM(tcpresp4); + + udpreq6 = json_object_new_object(); + CHECKMEM(udpreq6); + + udpresp6 = json_object_new_object(); + CHECKMEM(udpresp6); + + tcpreq6 = json_object_new_object(); + CHECKMEM(tcpreq6); + + tcpresp6 = json_object_new_object(); + CHECKMEM(tcpresp6); + + CHECK(dump_counters( + server->sctx->udpinstats4, isc_statsformat_json, + udpreq4, NULL, udpinsizestats_xmldesc, + dns_sizecounter_in_max, udpinsizestats_index, + udpinsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->udpoutstats4, isc_statsformat_json, + udpresp4, NULL, udpoutsizestats_xmldesc, + dns_sizecounter_out_max, udpoutsizestats_index, + udpoutsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->tcpinstats4, isc_statsformat_json, + tcpreq4, NULL, tcpinsizestats_xmldesc, + dns_sizecounter_in_max, tcpinsizestats_index, + tcpinsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->tcpoutstats4, isc_statsformat_json, + tcpresp4, NULL, tcpoutsizestats_xmldesc, + dns_sizecounter_out_max, tcpoutsizestats_index, + tcpoutsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->udpinstats6, isc_statsformat_json, + udpreq6, NULL, udpinsizestats_xmldesc, + dns_sizecounter_in_max, udpinsizestats_index, + udpinsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->udpoutstats6, isc_statsformat_json, + udpresp6, NULL, udpoutsizestats_xmldesc, + dns_sizecounter_out_max, udpoutsizestats_index, + udpoutsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->tcpinstats6, isc_statsformat_json, + tcpreq6, NULL, tcpinsizestats_xmldesc, + dns_sizecounter_in_max, tcpinsizestats_index, + tcpinsizestat_values, 0)); + + CHECK(dump_counters( + server->sctx->tcpoutstats6, isc_statsformat_json, + tcpresp6, NULL, tcpoutsizestats_xmldesc, + dns_sizecounter_out_max, tcpoutsizestats_index, + tcpoutsizestat_values, 0)); + + json_object_object_add(traffic, + "dns-udp-requests-sizes-received-ipv4", + udpreq4); + json_object_object_add( + traffic, "dns-udp-responses-sizes-sent-ipv4", udpresp4); + json_object_object_add(traffic, + "dns-tcp-requests-sizes-received-ipv4", + tcpreq4); + json_object_object_add( + traffic, "dns-tcp-responses-sizes-sent-ipv4", tcpresp4); + json_object_object_add(traffic, + "dns-udp-requests-sizes-received-ipv6", + udpreq6); + json_object_object_add( + traffic, "dns-udp-responses-sizes-sent-ipv6", udpresp6); + json_object_object_add(traffic, + "dns-tcp-requests-sizes-received-ipv6", + tcpreq6); + json_object_object_add( + traffic, "dns-tcp-responses-sizes-sent-ipv6", tcpresp6); + json_object_object_add(bindstats, "traffic", traffic); + udpreq4 = NULL; + udpresp4 = NULL; + tcpreq4 = NULL; + tcpresp4 = NULL; + udpreq6 = NULL; + udpresp6 = NULL; + tcpreq6 = NULL; + tcpresp6 = NULL; + traffic = NULL; + } + + *msg = json_object_to_json_string_ext(bindstats, + JSON_C_TO_STRING_PRETTY); + *msglen = strlen(*msg); + + if (rootp != NULL) { + *rootp = bindstats; + bindstats = NULL; + } + + result = ISC_R_SUCCESS; + +cleanup: + if (udpreq4 != NULL) { + json_object_put(udpreq4); + } + if (udpresp4 != NULL) { + json_object_put(udpresp4); + } + if (tcpreq4 != NULL) { + json_object_put(tcpreq4); + } + if (tcpresp4 != NULL) { + json_object_put(tcpresp4); + } + if (udpreq6 != NULL) { + json_object_put(udpreq6); + } + if (udpresp6 != NULL) { + json_object_put(udpresp6); + } + if (tcpreq6 != NULL) { + json_object_put(tcpreq6); + } + if (tcpresp6 != NULL) { + json_object_put(tcpresp6); + } + if (traffic != NULL) { + json_object_put(traffic); + } + if (bindstats != NULL) { + json_object_put(bindstats); + } + + return (result); +} + +static isc_result_t +render_json(uint32_t flags, void *arg, unsigned int *retcode, + const char **retmsg, const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + isc_result_t result; + json_object *bindstats = NULL; + named_server_t *server = arg; + const char *msg = NULL; + size_t msglen = 0; + char *p; + + result = generatejson(server, &msglen, &msg, &bindstats, flags); + if (result == ISC_R_SUCCESS) { + *retcode = 200; + *retmsg = "OK"; + *mimetype = "application/json"; + DE_CONST(msg, p); + isc_buffer_reinit(b, p, msglen); + isc_buffer_add(b, msglen); + *freecb = wrap_jsonfree; + *freecb_args = bindstats; + } else { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed at rendering JSON()"); + } + + return (result); +} + +static isc_result_t +render_json_all(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_ALL, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_json_status(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_STATUS, arg, retcode, retmsg, mimetype, + b, freecb, freecb_args)); +} + +static isc_result_t +render_json_server(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_SERVER, arg, retcode, retmsg, mimetype, + b, freecb, freecb_args)); +} + +static isc_result_t +render_json_zones(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_ZONES, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_json_mem(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_MEM, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_json_tasks(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_TASKS, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_json_net(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_NET, arg, retcode, retmsg, mimetype, b, + freecb, freecb_args)); +} + +static isc_result_t +render_json_traffic(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, + void *arg, unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) { + UNUSED(httpd); + UNUSED(urlinfo); + return (render_json(STATS_JSON_TRAFFIC, arg, retcode, retmsg, mimetype, + b, freecb, freecb_args)); +} + +#endif /* HAVE_JSON_C */ + +static isc_result_t +render_xsl(const isc_httpd_t *httpd, const isc_httpdurl_t *urlinfo, void *args, + unsigned int *retcode, const char **retmsg, const char **mimetype, + isc_buffer_t *b, isc_httpdfree_t **freecb, void **freecb_args) { + isc_result_t result; + char *p = NULL; + + UNUSED(httpd); + UNUSED(args); + + *freecb = NULL; + *freecb_args = NULL; + *mimetype = "text/xslt+xml"; + + if (isc_httpdurl_isstatic(urlinfo)) { + time_t t1, t2; + const isc_time_t *when; + const isc_time_t *loadtime; + + when = isc_httpd_if_modified_since(httpd); + + if (isc_time_isepoch(when)) { + goto send; + } + + result = isc_time_secondsastimet(when, &t1); + if (result != ISC_R_SUCCESS) { + goto send; + } + + loadtime = isc_httpdurl_loadtime(urlinfo); + + result = isc_time_secondsastimet(loadtime, &t2); + if (result != ISC_R_SUCCESS) { + goto send; + } + + if (t1 < t2) { + goto send; + } + + *retcode = 304; + *retmsg = "Not modified"; + goto end; + } + +send: + *retcode = 200; + *retmsg = "OK"; + DE_CONST(xslmsg, p); + isc_buffer_reinit(b, p, strlen(xslmsg)); + isc_buffer_add(b, strlen(xslmsg)); +end: + return (ISC_R_SUCCESS); +} + +static void +shutdown_listener(named_statschannel_t *listener) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&listener->address, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE, + "stopping statistics channel on %s", socktext); + + isc_httpdmgr_shutdown(&listener->httpdmgr); +} + +static bool +client_ok(const isc_sockaddr_t *fromaddr, void *arg) { + named_statschannel_t *listener = arg; + dns_aclenv_t *env = + ns_interfacemgr_getaclenv(named_g_server->interfacemgr); + isc_netaddr_t netaddr; + char socktext[ISC_SOCKADDR_FORMATSIZE]; + int match; + + REQUIRE(listener != NULL); + + isc_netaddr_fromsockaddr(&netaddr, fromaddr); + + LOCK(&listener->lock); + if ((dns_acl_match(&netaddr, NULL, listener->acl, env, &match, NULL) == + ISC_R_SUCCESS) && + match > 0) + { + UNLOCK(&listener->lock); + return (true); + } + UNLOCK(&listener->lock); + + isc_sockaddr_format(fromaddr, socktext, sizeof(socktext)); + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "rejected statistics connection from %s", socktext); + + return (false); +} + +static void +destroy_listener(void *arg) { + named_statschannel_t *listener = (named_statschannel_t *)arg; + + REQUIRE(listener != NULL); + REQUIRE(!ISC_LINK_LINKED(listener, link)); + + /* We don't have to acquire the lock here since it's already unlinked */ + dns_acl_detach(&listener->acl); + + isc_mutex_destroy(&listener->lock); + isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); +} + +static isc_result_t +add_listener(named_server_t *server, named_statschannel_t **listenerp, + const cfg_obj_t *listen_params, const cfg_obj_t *config, + isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, + const char *socktext) { + isc_result_t result; + named_statschannel_t *listener = NULL; + const cfg_obj_t *allow = NULL; + dns_acl_t *new_acl = NULL; + int pf; + + listener = isc_mem_get(server->mctx, sizeof(*listener)); + *listener = (named_statschannel_t){ .address = *addr }; + ISC_LINK_INIT(listener, link); + isc_mutex_init(&listener->lock); + isc_mem_attach(server->mctx, &listener->mctx); + + allow = cfg_tuple_get(listen_params, "allow"); + if (allow != NULL && cfg_obj_islist(allow)) { + result = cfg_acl_fromconfig(allow, config, named_g_lctx, + aclconfctx, listener->mctx, 0, + &new_acl); + } else { + result = dns_acl_any(listener->mctx, &new_acl); + } + CHECK(result); + + dns_acl_attach(new_acl, &listener->acl); + dns_acl_detach(&new_acl); + + pf = isc_sockaddr_pf(&listener->address); + if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || + (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) + { + CHECK(ISC_R_FAMILYNOSUPPORT); + } + + CHECK(isc_httpdmgr_create(named_g_netmgr, server->mctx, addr, client_ok, + destroy_listener, listener, + &listener->httpdmgr)); + +#ifdef HAVE_LIBXML2 + isc_httpdmgr_addurl(listener->httpdmgr, "/", false, render_xml_all, + server); + isc_httpdmgr_addurl(listener->httpdmgr, "/xml", false, render_xml_all, + server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR, false, + render_xml_all, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/status", false, + render_xml_status, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/server", false, + render_xml_server, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/zones", false, + render_xml_zones, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/net", false, + render_xml_net, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/tasks", false, + render_xml_tasks, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/mem", false, + render_xml_mem, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/xml/v" STATS_XML_VERSION_MAJOR "/traffic", false, + render_xml_traffic, server); +#endif /* ifdef HAVE_LIBXML2 */ +#ifdef HAVE_JSON_C + isc_httpdmgr_addurl(listener->httpdmgr, "/json", false, render_json_all, + server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR, false, + render_json_all, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/status", false, + render_json_status, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/server", false, + render_json_server, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/zones", false, + render_json_zones, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/tasks", false, + render_json_tasks, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/net", false, + render_json_net, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/mem", false, + render_json_mem, server); + isc_httpdmgr_addurl(listener->httpdmgr, + "/json/v" STATS_JSON_VERSION_MAJOR "/traffic", + false, render_json_traffic, server); +#endif /* ifdef HAVE_JSON_C */ + isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", true, render_xsl, + server); + + *listenerp = listener; + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_NOTICE, + "statistics channel listening on %s", socktext); + + return (ISC_R_SUCCESS); + +cleanup: + if (listener->acl != NULL) { + dns_acl_detach(&listener->acl); + } + isc_mutex_destroy(&listener->lock); + isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); + + return (result); +} + +static void +update_listener(named_server_t *server, named_statschannel_t **listenerp, + const cfg_obj_t *listen_params, const cfg_obj_t *config, + isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, + const char *socktext) { + named_statschannel_t *listener; + const cfg_obj_t *allow = NULL; + dns_acl_t *new_acl = NULL; + isc_result_t result = ISC_R_SUCCESS; + + for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL; + listener = ISC_LIST_NEXT(listener, link)) + { + if (isc_sockaddr_equal(addr, &listener->address)) { + break; + } + } + + if (listener == NULL) { + *listenerp = NULL; + return; + } + + /* + * Now, keep the old access list unless a new one can be made. + */ + allow = cfg_tuple_get(listen_params, "allow"); + if (allow != NULL && cfg_obj_islist(allow)) { + result = cfg_acl_fromconfig(allow, config, named_g_lctx, + aclconfctx, listener->mctx, 0, + &new_acl); + } else { + result = dns_acl_any(listener->mctx, &new_acl); + } + + if (result == ISC_R_SUCCESS) { + LOCK(&listener->lock); + + dns_acl_detach(&listener->acl); + dns_acl_attach(new_acl, &listener->acl); + dns_acl_detach(&new_acl); + + UNLOCK(&listener->lock); + } else { + cfg_obj_log(listen_params, named_g_lctx, ISC_LOG_WARNING, + "couldn't install new acl for " + "statistics channel %s: %s", + socktext, isc_result_totext(result)); + } + + *listenerp = listener; +} + +isc_result_t +named_statschannels_configure(named_server_t *server, const cfg_obj_t *config, + cfg_aclconfctx_t *aclconfctx) { + named_statschannel_t *listener, *listener_next; + named_statschannellist_t new_listeners; + const cfg_obj_t *statschannellist = NULL; + const cfg_listelt_t *element, *element2; + char socktext[ISC_SOCKADDR_FORMATSIZE]; + + RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS); + + ISC_LIST_INIT(new_listeners); + + /* + * Get the list of named.conf 'statistics-channels' statements. + */ + (void)cfg_map_get(config, "statistics-channels", &statschannellist); + + /* + * Run through the new address/port list, noting sockets that are + * already being listened on and moving them to the new list. + * + * Identifying duplicate addr/port combinations is left to either + * the underlying config code, or to the bind attempt getting an + * address-in-use error. + */ + if (statschannellist != NULL) { +#ifndef EXTENDED_STATS + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "statistics-channels specified but not effective " + "due to missing XML and/or JSON library"); +#else /* EXTENDED_STATS */ +#ifndef HAVE_LIBXML2 + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "statistics-channels: XML library missing, " + "only JSON stats will be available"); +#endif /* !HAVE_LIBXML2 */ +#ifndef HAVE_JSON_C + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "statistics-channels: JSON library missing, " + "only XML stats will be available"); +#endif /* !HAVE_JSON_C */ +#endif /* EXTENDED_STATS */ + + for (element = cfg_list_first(statschannellist); + element != NULL; element = cfg_list_next(element)) + { + const cfg_obj_t *statschannel; + const cfg_obj_t *listenercfg = NULL; + + statschannel = cfg_listelt_value(element); + (void)cfg_map_get(statschannel, "inet", &listenercfg); + if (listenercfg == NULL) { + continue; + } + + for (element2 = cfg_list_first(listenercfg); + element2 != NULL; + element2 = cfg_list_next(element2)) + { + const cfg_obj_t *listen_params; + const cfg_obj_t *obj; + isc_sockaddr_t addr; + + listen_params = cfg_listelt_value(element2); + + obj = cfg_tuple_get(listen_params, "address"); + addr = *cfg_obj_assockaddr(obj); + if (isc_sockaddr_getport(&addr) == 0) { + isc_sockaddr_setport( + &addr, + NAMED_STATSCHANNEL_HTTPPORT); + } + + isc_sockaddr_format(&addr, socktext, + sizeof(socktext)); + + isc_log_write(named_g_lctx, + NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, + ISC_LOG_DEBUG(9), + "processing statistics " + "channel %s", + socktext); + + update_listener(server, &listener, + listen_params, config, &addr, + aclconfctx, socktext); + + if (listener != NULL) { + /* + * Remove the listener from the old + * list, so it won't be shut down. + */ + ISC_LIST_UNLINK(server->statschannels, + listener, link); + } else { + /* + * This is a new listener. + */ + isc_result_t r; + + r = add_listener(server, &listener, + listen_params, config, + &addr, aclconfctx, + socktext); + if (r != ISC_R_SUCCESS) { + cfg_obj_log( + listen_params, + named_g_lctx, + ISC_LOG_WARNING, + "couldn't allocate " + "statistics channel" + " %s: %s", + socktext, + isc_result_totext(r)); + } + } + + if (listener != NULL) { + ISC_LIST_APPEND(new_listeners, listener, + link); + } + } + } + } + + for (listener = ISC_LIST_HEAD(server->statschannels); listener != NULL; + listener = listener_next) + { + listener_next = ISC_LIST_NEXT(listener, link); + ISC_LIST_UNLINK(server->statschannels, listener, link); + shutdown_listener(listener); + } + + ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link); + return (ISC_R_SUCCESS); +} + +void +named_statschannels_shutdown(named_server_t *server) { + named_statschannel_t *listener; + + while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) { + ISC_LIST_UNLINK(server->statschannels, listener, link); + shutdown_listener(listener); + } +} + +isc_result_t +named_stats_dump(named_server_t *server, FILE *fp) { + isc_stdtime_t now; + isc_result_t result; + dns_view_t *view; + dns_zone_t *zone, *next; + stats_dumparg_t dumparg; + uint64_t nsstat_values[ns_statscounter_max]; + uint64_t resstat_values[dns_resstatscounter_max]; + uint64_t adbstat_values[dns_adbstats_max]; + uint64_t zonestat_values[dns_zonestatscounter_max]; + uint64_t sockstat_values[isc_sockstatscounter_max]; + uint64_t gluecachestats_values[dns_gluecachestatscounter_max]; + + RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS); + + /* Set common fields */ + dumparg.type = isc_statsformat_file; + dumparg.arg = fp; + + isc_stdtime_get(&now); + fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now); + + fprintf(fp, "++ Incoming Requests ++\n"); + dns_opcodestats_dump(server->sctx->opcodestats, opcodestat_dump, + &dumparg, 0); + + fprintf(fp, "++ Incoming Queries ++\n"); + dns_rdatatypestats_dump(server->sctx->rcvquerystats, rdtypestat_dump, + &dumparg, 0); + + fprintf(fp, "++ Outgoing Rcodes ++\n"); + dns_rcodestats_dump(server->sctx->rcodestats, rcodestat_dump, &dumparg, + 0); + + fprintf(fp, "++ Outgoing Queries ++\n"); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (view->resquerystats == NULL) { + continue; + } + if (strcmp(view->name, "_default") == 0) { + fprintf(fp, "[View: default]\n"); + } else { + fprintf(fp, "[View: %s]\n", view->name); + } + dns_rdatatypestats_dump(view->resquerystats, rdtypestat_dump, + &dumparg, 0); + } + + fprintf(fp, "++ Name Server Statistics ++\n"); + (void)dump_counters(ns_stats_get(server->sctx->nsstats), + isc_statsformat_file, fp, NULL, nsstats_desc, + ns_statscounter_max, nsstats_index, nsstat_values, + 0); + + fprintf(fp, "++ Zone Maintenance Statistics ++\n"); + (void)dump_counters(server->zonestats, isc_statsformat_file, fp, NULL, + zonestats_desc, dns_zonestatscounter_max, + zonestats_index, zonestat_values, 0); + + fprintf(fp, "++ Resolver Statistics ++\n"); + fprintf(fp, "[Common]\n"); + (void)dump_counters(server->resolverstats, isc_statsformat_file, fp, + NULL, resstats_desc, dns_resstatscounter_max, + resstats_index, resstat_values, 0); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (view->resstats == NULL) { + continue; + } + if (strcmp(view->name, "_default") == 0) { + fprintf(fp, "[View: default]\n"); + } else { + fprintf(fp, "[View: %s]\n", view->name); + } + (void)dump_counters(view->resstats, isc_statsformat_file, fp, + NULL, resstats_desc, + dns_resstatscounter_max, resstats_index, + resstat_values, 0); + } + + fprintf(fp, "++ Cache Statistics ++\n"); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (strcmp(view->name, "_default") == 0) { + fprintf(fp, "[View: default]\n"); + } else { + fprintf(fp, "[View: %s (Cache: %s)]\n", view->name, + dns_cache_getname(view->cache)); + } + /* + * Avoid dumping redundant statistics when the cache is shared. + */ + if (dns_view_iscacheshared(view)) { + continue; + } + dns_cache_dumpstats(view->cache, fp); + } + + fprintf(fp, "++ Cache DB RRsets ++\n"); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + dns_stats_t *cacherrstats; + + cacherrstats = dns_db_getrrsetstats(view->cachedb); + if (cacherrstats == NULL) { + continue; + } + if (strcmp(view->name, "_default") == 0) { + fprintf(fp, "[View: default]\n"); + } else { + fprintf(fp, "[View: %s (Cache: %s)]\n", view->name, + dns_cache_getname(view->cache)); + } + if (dns_view_iscacheshared(view)) { + /* + * Avoid dumping redundant statistics when the cache is + * shared. + */ + continue; + } + dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump, + &dumparg, 0); + } + + fprintf(fp, "++ ADB stats ++\n"); + for (view = ISC_LIST_HEAD(server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + if (view->adbstats == NULL) { + continue; + } + if (strcmp(view->name, "_default") == 0) { + fprintf(fp, "[View: default]\n"); + } else { + fprintf(fp, "[View: %s]\n", view->name); + } + (void)dump_counters(view->adbstats, isc_statsformat_file, fp, + NULL, adbstats_desc, dns_adbstats_max, + adbstats_index, adbstat_values, 0); + } + + fprintf(fp, "++ Socket I/O Statistics ++\n"); + (void)dump_counters(server->sockstats, isc_statsformat_file, fp, NULL, + sockstats_desc, isc_sockstatscounter_max, + sockstats_index, sockstat_values, 0); + + fprintf(fp, "++ Per Zone Query Statistics ++\n"); + zone = NULL; + for (result = dns_zone_first(server->zonemgr, &zone); + result == ISC_R_SUCCESS; + next = NULL, result = dns_zone_next(zone, &next), zone = next) + { + isc_stats_t *zonestats = dns_zone_getrequeststats(zone); + if (zonestats != NULL) { + char zonename[DNS_NAME_FORMATSIZE]; + + view = dns_zone_getview(zone); + if (view == NULL) { + continue; + } + + dns_name_format(dns_zone_getorigin(zone), zonename, + sizeof(zonename)); + fprintf(fp, "[%s", zonename); + if (strcmp(view->name, "_default") != 0) { + fprintf(fp, " (view: %s)", view->name); + } + fprintf(fp, "]\n"); + + (void)dump_counters(zonestats, isc_statsformat_file, fp, + NULL, nsstats_desc, + ns_statscounter_max, nsstats_index, + nsstat_values, 0); + } + } + + fprintf(fp, "++ Per Zone Glue Cache Statistics ++\n"); + zone = NULL; + for (result = dns_zone_first(server->zonemgr, &zone); + result == ISC_R_SUCCESS; + next = NULL, result = dns_zone_next(zone, &next), zone = next) + { + isc_stats_t *gluecachestats = dns_zone_getgluecachestats(zone); + if (gluecachestats != NULL) { + char zonename[DNS_NAME_FORMATSIZE]; + + view = dns_zone_getview(zone); + if (view == NULL) { + continue; + } + + dns_name_format(dns_zone_getorigin(zone), zonename, + sizeof(zonename)); + fprintf(fp, "[%s", zonename); + if (strcmp(view->name, "_default") != 0) { + fprintf(fp, " (view: %s)", view->name); + } + fprintf(fp, "]\n"); + + (void)dump_counters( + gluecachestats, isc_statsformat_file, fp, NULL, + gluecachestats_desc, + dns_gluecachestatscounter_max, + gluecachestats_index, gluecachestats_values, 0); + } + } + + fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now); + + return (ISC_R_SUCCESS); /* this function currently always succeeds */ +} diff --git a/bin/named/tkeyconf.c b/bin/named/tkeyconf.c new file mode 100644 index 0000000..03b198b --- /dev/null +++ b/bin/named/tkeyconf.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include +#include /* Required for HP/UX (and others?) */ + +#include +#include +#include +#include + +#include + +#include + +#include + +#define RETERR(x) \ + do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto failure; \ + } while (0) + +#include +#define LOG(msg) \ + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \ + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "%s", msg) + +isc_result_t +named_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx, + dns_tkeyctx_t **tctxp) { + isc_result_t result; + dns_tkeyctx_t *tctx = NULL; + const char *s; + uint32_t n; + dns_fixedname_t fname; + dns_name_t *name; + isc_buffer_t b; + const cfg_obj_t *obj; + int type; + + result = dns_tkeyctx_create(mctx, &tctx); + if (result != ISC_R_SUCCESS) { + return (result); + } + + obj = NULL; + result = cfg_map_get(options, "tkey-dhkey", &obj); + if (result == ISC_R_SUCCESS) { + s = cfg_obj_asstring(cfg_tuple_get(obj, "name")); + n = cfg_obj_asuint32(cfg_tuple_get(obj, "keyid")); + isc_buffer_constinit(&b, s, strlen(s)); + isc_buffer_add(&b, strlen(s)); + name = dns_fixedname_initname(&fname); + RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + type = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY; + RETERR(dst_key_fromfile(name, (dns_keytag_t)n, DNS_KEYALG_DH, + type, NULL, mctx, &tctx->dhkey)); + } + + obj = NULL; + result = cfg_map_get(options, "tkey-domain", &obj); + if (result == ISC_R_SUCCESS) { + s = cfg_obj_asstring(obj); + isc_buffer_constinit(&b, s, strlen(s)); + isc_buffer_add(&b, strlen(s)); + name = dns_fixedname_initname(&fname); + RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + tctx->domain = isc_mem_get(mctx, sizeof(dns_name_t)); + dns_name_init(tctx->domain, NULL); + dns_name_dup(name, mctx, tctx->domain); + } + + obj = NULL; + result = cfg_map_get(options, "tkey-gssapi-credential", &obj); + if (result == ISC_R_SUCCESS) { + s = cfg_obj_asstring(obj); + + isc_buffer_constinit(&b, s, strlen(s)); + isc_buffer_add(&b, strlen(s)); + name = dns_fixedname_initname(&fname); + RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + RETERR(dst_gssapi_acquirecred(name, false, &tctx->gsscred)); + } + + obj = NULL; + result = cfg_map_get(options, "tkey-gssapi-keytab", &obj); + if (result == ISC_R_SUCCESS) { + s = cfg_obj_asstring(obj); + tctx->gssapi_keytab = isc_mem_strdup(mctx, s); + } + + *tctxp = tctx; + return (ISC_R_SUCCESS); + +failure: + dns_tkeyctx_destroy(&tctx); + return (result); +} diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c new file mode 100644 index 0000000..f24aab1 --- /dev/null +++ b/bin/named/transportconf.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#define create_name(id, name) \ + isc_buffer_t namesrc, namebuf; \ + char namedata[DNS_NAME_FORMATSIZE + 1]; \ + dns_name_init(name, NULL); \ + isc_buffer_constinit(&namesrc, id, strlen(id)); \ + isc_buffer_add(&namesrc, strlen(id)); \ + isc_buffer_init(&namebuf, namedata, sizeof(namedata)); \ + result = (dns_name_fromtext(name, &namesrc, dns_rootname, \ + DNS_NAME_DOWNCASE, &namebuf)); \ + if (result != ISC_R_SUCCESS) { \ + goto failure; \ + } + +#define parse_transport_option(map, transport, name, setter) \ + { \ + const cfg_obj_t *obj = NULL; \ + cfg_map_get(map, name, &obj); \ + if (obj != NULL) { \ + setter(transport, cfg_obj_asstring(obj)); \ + } \ + } + +#define parse_transport_tls_versions(map, transport, name, setter) \ + { \ + const cfg_obj_t *obj = NULL; \ + cfg_map_get(map, name, &obj); \ + if (obj != NULL) { \ + { \ + uint32_t tls_protos = 0; \ + const cfg_listelt_t *proto = NULL; \ + INSIST(obj != NULL); \ + for (proto = cfg_list_first(obj); proto != 0; \ + proto = cfg_list_next(proto)) \ + { \ + const cfg_obj_t *tls_proto_obj = \ + cfg_listelt_value(proto); \ + const char *tls_sver = \ + cfg_obj_asstring( \ + tls_proto_obj); \ + const isc_tls_protocol_version_t ver = \ + isc_tls_protocol_name_to_version( \ + tls_sver); \ + INSIST(ver != \ + ISC_TLS_PROTO_VER_UNDEFINED); \ + INSIST(isc_tls_protocol_supported( \ + ver)); \ + tls_protos |= ver; \ + } \ + if (tls_protos != 0) { \ + setter(transport, tls_protos); \ + } \ + } \ + } \ + } + +#define parse_transport_bool_option(map, transport, name, setter) \ + { \ + const cfg_obj_t *obj = NULL; \ + cfg_map_get(map, name, &obj); \ + if (obj != NULL) { \ + setter(transport, cfg_obj_asboolean(obj)); \ + } \ + } + +static isc_result_t +add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { + const cfg_obj_t *doh = NULL; + const char *dohid = NULL; + isc_result_t result; + + for (const cfg_listelt_t *element = cfg_list_first(transportlist); + element != NULL; element = cfg_list_next(element)) + { + dns_name_t dohname; + dns_transport_t *transport; + + doh = cfg_listelt_value(element); + dohid = cfg_obj_asstring(cfg_map_getname(doh)); + + create_name(dohid, &dohname); + + transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP, + list); + + dns_transport_set_tlsname(transport, dohid); + parse_transport_option(doh, transport, "key-file", + dns_transport_set_keyfile); + parse_transport_option(doh, transport, "cert-file", + dns_transport_set_certfile); + parse_transport_tls_versions(doh, transport, "protocols", + dns_transport_set_tls_versions); + parse_transport_option(doh, transport, "ciphers", + dns_transport_set_ciphers); + parse_transport_bool_option( + doh, transport, "prefer-server-ciphers", + dns_transport_set_prefer_server_ciphers) + parse_transport_option(doh, transport, "ca-file", + dns_transport_set_cafile); + parse_transport_option(doh, transport, "remote-hostname", + dns_transport_set_remote_hostname); + } + + return (ISC_R_SUCCESS); +failure: + cfg_obj_log(doh, named_g_lctx, ISC_LOG_ERROR, + "configuring DoH '%s': %s", dohid, + isc_result_totext(result)); + + return (result); +} + +static isc_result_t +add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { + const cfg_obj_t *tls = NULL; + const char *tlsid = NULL; + isc_result_t result; + + for (const cfg_listelt_t *element = cfg_list_first(transportlist); + element != NULL; element = cfg_list_next(element)) + { + dns_name_t tlsname; + dns_transport_t *transport; + + tls = cfg_listelt_value(element); + tlsid = cfg_obj_asstring(cfg_map_getname(tls)); + + if (!strcmp(tlsid, "ephemeral")) { + result = ISC_R_UNEXPECTEDTOKEN; + goto failure; + } + + create_name(tlsid, &tlsname); + + transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, + list); + + dns_transport_set_tlsname(transport, tlsid); + parse_transport_option(tls, transport, "key-file", + dns_transport_set_keyfile); + parse_transport_option(tls, transport, "cert-file", + dns_transport_set_certfile); + parse_transport_tls_versions(tls, transport, "protocols", + dns_transport_set_tls_versions); + parse_transport_option(tls, transport, "ciphers", + dns_transport_set_ciphers); + parse_transport_bool_option( + tls, transport, "prefer-server-ciphers", + dns_transport_set_prefer_server_ciphers) + parse_transport_option(tls, transport, "ca-file", + dns_transport_set_cafile); + parse_transport_option(tls, transport, "remote-hostname", + dns_transport_set_remote_hostname); + } + + return (ISC_R_SUCCESS); +failure: + cfg_obj_log(tls, named_g_lctx, ISC_LOG_ERROR, + "configuring tls '%s': %s", tlsid, + isc_result_totext(result)); + + return (result); +} + +#define CHECK(f) \ + if ((result = f) != ISC_R_SUCCESS) { \ + goto failure; \ + } + +static isc_result_t +transport_list_fromconfig(const cfg_obj_t *config, dns_transport_list_t *list) { + const cfg_obj_t *obj = NULL; + isc_result_t result = ISC_R_SUCCESS; + + if (result == ISC_R_SUCCESS && + cfg_map_get(config, "tls", &obj) == ISC_R_SUCCESS) + { + result = add_tls_transports(obj, list); + obj = NULL; + } + + if (result == ISC_R_SUCCESS && + cfg_map_get(config, "doh", &obj) == ISC_R_SUCCESS) + { + result = add_doh_transports(obj, list); + obj = NULL; + } + + return (result); +} + +static void +transport_list_add_ephemeral(dns_transport_list_t *list) { + isc_result_t result; + dns_name_t tlsname; + dns_transport_t *transport; + + create_name("ephemeral", &tlsname); + + transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list); + dns_transport_set_tlsname(transport, "ephemeral"); + + return; +failure: + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +isc_result_t +named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_transport_list_t **listp) { + isc_result_t result; + dns_transport_list_t *list = dns_transport_list_new(mctx); + + REQUIRE(listp != NULL && *listp == NULL); + + transport_list_add_ephemeral(list); + + if (config != NULL) { + result = transport_list_fromconfig(config, list); + if (result != ISC_R_SUCCESS) { + goto failure; + } + } + + if (vconfig != NULL) { + config = cfg_tuple_get(vconfig, "options"); + transport_list_fromconfig(config, list); + } + + *listp = list; + return (ISC_R_SUCCESS); +failure: + dns_transport_list_detach(&list); + return (result); +} diff --git a/bin/named/tsigconf.c b/bin/named/tsigconf.c new file mode 100644 index 0000000..6d596ab --- /dev/null +++ b/bin/named/tsigconf.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +static isc_result_t +add_initial_keys(const cfg_obj_t *list, dns_tsig_keyring_t *ring, + isc_mem_t *mctx) { + dns_tsigkey_t *tsigkey = NULL; + const cfg_listelt_t *element; + const cfg_obj_t *key = NULL; + const char *keyid = NULL; + unsigned char *secret = NULL; + int secretalloc = 0; + int secretlen = 0; + isc_result_t ret; + isc_stdtime_t now; + uint16_t bits; + + for (element = cfg_list_first(list); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *algobj = NULL; + const cfg_obj_t *secretobj = NULL; + dns_name_t keyname; + const dns_name_t *alg; + const char *algstr; + char keynamedata[1024]; + isc_buffer_t keynamesrc, keynamebuf; + const char *secretstr; + isc_buffer_t secretbuf; + + key = cfg_listelt_value(element); + keyid = cfg_obj_asstring(cfg_map_getname(key)); + + algobj = NULL; + secretobj = NULL; + (void)cfg_map_get(key, "algorithm", &algobj); + (void)cfg_map_get(key, "secret", &secretobj); + INSIST(algobj != NULL && secretobj != NULL); + + /* + * Create the key name. + */ + dns_name_init(&keyname, NULL); + isc_buffer_constinit(&keynamesrc, keyid, strlen(keyid)); + isc_buffer_add(&keynamesrc, strlen(keyid)); + isc_buffer_init(&keynamebuf, keynamedata, sizeof(keynamedata)); + ret = dns_name_fromtext(&keyname, &keynamesrc, dns_rootname, + DNS_NAME_DOWNCASE, &keynamebuf); + if (ret != ISC_R_SUCCESS) { + goto failure; + } + + /* + * Create the algorithm. + */ + algstr = cfg_obj_asstring(algobj); + if (named_config_getkeyalgorithm(algstr, &alg, &bits) != + ISC_R_SUCCESS) + { + cfg_obj_log(algobj, named_g_lctx, ISC_LOG_ERROR, + "key '%s': has a " + "unsupported algorithm '%s'", + keyid, algstr); + ret = DNS_R_BADALG; + goto failure; + } + + secretstr = cfg_obj_asstring(secretobj); + secretalloc = secretlen = strlen(secretstr) * 3 / 4; + secret = isc_mem_get(mctx, secretlen); + isc_buffer_init(&secretbuf, secret, secretlen); + ret = isc_base64_decodestring(secretstr, &secretbuf); + if (ret != ISC_R_SUCCESS) { + goto failure; + } + secretlen = isc_buffer_usedlength(&secretbuf); + + isc_stdtime_get(&now); + ret = dns_tsigkey_create(&keyname, alg, secret, secretlen, + false, NULL, now, now, mctx, ring, + &tsigkey); + isc_mem_put(mctx, secret, secretalloc); + secret = NULL; + if (ret != ISC_R_SUCCESS) { + goto failure; + } + /* + * Set digest bits. + */ + dst_key_setbits(tsigkey->key, bits); + dns_tsigkey_detach(&tsigkey); + } + + return (ISC_R_SUCCESS); + +failure: + cfg_obj_log(key, named_g_lctx, ISC_LOG_ERROR, + "configuring key '%s': %s", keyid, isc_result_totext(ret)); + + if (secret != NULL) { + isc_mem_put(mctx, secret, secretalloc); + } + return (ret); +} + +isc_result_t +named_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig, + isc_mem_t *mctx, dns_tsig_keyring_t **ringp) { + const cfg_obj_t *maps[3]; + const cfg_obj_t *keylist; + dns_tsig_keyring_t *ring = NULL; + isc_result_t result; + int i; + + REQUIRE(ringp != NULL && *ringp == NULL); + + i = 0; + if (config != NULL) { + maps[i++] = config; + } + if (vconfig != NULL) { + maps[i++] = cfg_tuple_get(vconfig, "options"); + } + maps[i] = NULL; + + result = dns_tsigkeyring_create(mctx, &ring); + if (result != ISC_R_SUCCESS) { + return (result); + } + + for (i = 0;; i++) { + if (maps[i] == NULL) { + break; + } + keylist = NULL; + result = cfg_map_get(maps[i], "key", &keylist); + if (result != ISC_R_SUCCESS) { + continue; + } + result = add_initial_keys(keylist, ring, mctx); + if (result != ISC_R_SUCCESS) { + goto failure; + } + } + + *ringp = ring; + return (ISC_R_SUCCESS); + +failure: + dns_tsigkeyring_detach(&ring); + return (result); +} diff --git a/bin/named/xsl_p.h b/bin/named/xsl_p.h new file mode 100644 index 0000000..5623534 --- /dev/null +++ b/bin/named/xsl_p.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +extern const char xslmsg[]; diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c new file mode 100644 index 0000000..44c2242 --- /dev/null +++ b/bin/named/zoneconf.c @@ -0,0 +1,2114 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* Required for HP/UX (and others?) */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* ACLs associated with zone */ +typedef enum { + allow_notify, + allow_query, + allow_query_on, + allow_transfer, + allow_update, + allow_update_forwarding +} acl_type_t; + +#define RETERR(x) \ + do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return ((_r)); \ + } while (0) + +#define CHECK(x) \ + do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +/*% + * Convenience function for configuring a single zone ACL. + */ +static isc_result_t +configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, + const cfg_obj_t *config, acl_type_t acltype, + cfg_aclconfctx_t *actx, dns_zone_t *zone, + void (*setzacl)(dns_zone_t *, dns_acl_t *), + void (*clearzacl)(dns_zone_t *)) { + isc_result_t result; + const cfg_obj_t *maps[5] = { NULL, NULL, NULL, NULL, NULL }; + const cfg_obj_t *aclobj = NULL; + int i = 0; + dns_acl_t **aclp = NULL, *acl = NULL; + const char *aclname; + dns_view_t *view; + + view = dns_zone_getview(zone); + + switch (acltype) { + case allow_notify: + if (view != NULL) { + aclp = &view->notifyacl; + } + aclname = "allow-notify"; + break; + case allow_query: + if (view != NULL) { + aclp = &view->queryacl; + } + aclname = "allow-query"; + break; + case allow_query_on: + if (view != NULL) { + aclp = &view->queryonacl; + } + aclname = "allow-query-on"; + break; + case allow_transfer: + if (view != NULL) { + aclp = &view->transferacl; + } + aclname = "allow-transfer"; + break; + case allow_update: + if (view != NULL) { + aclp = &view->updateacl; + } + aclname = "allow-update"; + break; + case allow_update_forwarding: + if (view != NULL) { + aclp = &view->upfwdacl; + } + aclname = "allow-update-forwarding"; + break; + default: + UNREACHABLE(); + } + + /* First check to see if ACL is defined within the zone */ + if (zconfig != NULL) { + maps[0] = cfg_tuple_get(zconfig, "options"); + (void)named_config_get(maps, aclname, &aclobj); + if (aclobj != NULL) { + aclp = NULL; + goto parse_acl; + } + } + + /* Failing that, see if there's a default ACL already in the view */ + if (aclp != NULL && *aclp != NULL) { + (*setzacl)(zone, *aclp); + return (ISC_R_SUCCESS); + } + + /* Check for default ACLs that haven't been parsed yet */ + if (vconfig != NULL) { + const cfg_obj_t *options = cfg_tuple_get(vconfig, "options"); + if (options != NULL) { + maps[i++] = options; + } + } + if (config != NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + maps[i++] = options; + } + } + maps[i++] = named_g_defaults; + maps[i] = NULL; + + (void)named_config_get(maps, aclname, &aclobj); + if (aclobj == NULL) { + (*clearzacl)(zone); + return (ISC_R_SUCCESS); + } + +parse_acl: + result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx, + named_g_mctx, 0, &acl); + if (result != ISC_R_SUCCESS) { + return (result); + } + (*setzacl)(zone, acl); + + /* Set the view default now */ + if (aclp != NULL) { + dns_acl_attach(acl, aclp); + } + + dns_acl_detach(&acl); + return (ISC_R_SUCCESS); +} + +/*% + * Parse the zone update-policy statement. + */ +static isc_result_t +configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone, + const char *zname) { + const cfg_obj_t *updatepolicy = NULL; + const cfg_listelt_t *element, *element2; + dns_ssutable_t *table = NULL; + isc_mem_t *mctx = dns_zone_getmctx(zone); + bool autoddns = false; + isc_result_t result = ISC_R_SUCCESS; + + (void)cfg_map_get(zconfig, "update-policy", &updatepolicy); + + if (updatepolicy == NULL) { + dns_zone_setssutable(zone, NULL); + return (ISC_R_SUCCESS); + } + + if (cfg_obj_isstring(updatepolicy) && + strcmp("local", cfg_obj_asstring(updatepolicy)) == 0) + { + autoddns = true; + updatepolicy = NULL; + } + + dns_ssutable_create(mctx, &table); + + for (element = cfg_list_first(updatepolicy); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *stmt = cfg_listelt_value(element); + const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode"); + const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity"); + const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype"); + const cfg_obj_t *dname = cfg_tuple_get(stmt, "name"); + const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types"); + const char *str; + bool grant = false; + bool usezone = false; + dns_ssumatchtype_t mtype = dns_ssumatchtype_name; + dns_fixedname_t fname, fident; + isc_buffer_t b; + dns_ssuruletype_t *types; + unsigned int i, n; + + str = cfg_obj_asstring(mode); + if (strcasecmp(str, "grant") == 0) { + grant = true; + } else if (strcasecmp(str, "deny") == 0) { + grant = false; + } else { + UNREACHABLE(); + } + + str = cfg_obj_asstring(matchtype); + CHECK(dns_ssu_mtypefromstring(str, &mtype)); + if (mtype == dns_ssumatchtype_subdomain && + strcasecmp(str, "zonesub") == 0) + { + usezone = true; + } + + dns_fixedname_init(&fident); + str = cfg_obj_asstring(identity); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(dns_fixedname_name(&fident), &b, + dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(identity, named_g_lctx, ISC_LOG_ERROR, + "'%s' is not a valid name", str); + goto cleanup; + } + + dns_fixedname_init(&fname); + if (usezone) { + dns_name_copy(dns_zone_getorigin(zone), + dns_fixedname_name(&fname)); + } else { + str = cfg_obj_asstring(dname); + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(dns_fixedname_name(&fname), + &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(identity, named_g_lctx, + ISC_LOG_ERROR, + "'%s' is not a valid name", str); + goto cleanup; + } + } + + n = named_config_listcount(typelist); + if (n == 0) { + types = NULL; + } else { + types = isc_mem_get(mctx, n * sizeof(*types)); + } + + i = 0; + for (element2 = cfg_list_first(typelist); element2 != NULL; + element2 = cfg_list_next(element2)) + { + const cfg_obj_t *typeobj; + const char *bracket; + isc_textregion_t r; + unsigned long max = 0; + + INSIST(i < n); + + typeobj = cfg_listelt_value(element2); + str = cfg_obj_asstring(typeobj); + DE_CONST(str, r.base); + + bracket = strchr(str, '(' /*)*/); + if (bracket != NULL) { + char *end = NULL; + r.length = bracket - str; + max = strtoul(bracket + 1, &end, 10); + if (max > 0xffff || end[0] != /*(*/ ')' || + end[1] != 0) + { + cfg_obj_log(identity, named_g_lctx, + ISC_LOG_ERROR, + "'%s' is not a valid count", + bracket); + isc_mem_put(mctx, types, + n * sizeof(*types)); + goto cleanup; + } + } else { + r.length = strlen(str); + } + types[i].max = max; + + result = dns_rdatatype_fromtext(&types[i++].type, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(identity, named_g_lctx, + ISC_LOG_ERROR, + "'%.*s' is not a valid type", + (int)r.length, str); + isc_mem_put(mctx, types, n * sizeof(*types)); + goto cleanup; + } + } + INSIST(i == n); + + dns_ssutable_addrule(table, grant, dns_fixedname_name(&fident), + mtype, dns_fixedname_name(&fname), n, + types); + if (types != NULL) { + isc_mem_put(mctx, types, n * sizeof(*types)); + } + } + + /* + * If "update-policy local;" and a session key exists, + * then use the default policy, which is equivalent to: + * update-policy { grant zonesub any; }; + */ + if (autoddns) { + dns_ssuruletype_t any = { dns_rdatatype_any, 0 }; + + if (named_g_server->session_keyname == NULL) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "failed to enable auto DDNS policy " + "for zone %s: session key not found", + zname); + result = ISC_R_NOTFOUND; + goto cleanup; + } + + dns_ssutable_addrule(table, true, + named_g_server->session_keyname, + dns_ssumatchtype_local, + dns_zone_getorigin(zone), 1, &any); + } + + dns_zone_setssutable(zone, table); + +cleanup: + dns_ssutable_detach(&table); + return (result); +} + +/* + * This is the TTL used for internally generated RRsets for static-stub zones. + * The value doesn't matter because the mapping is static, but needs to be + * defined for the sake of implementation. + */ +#define STATICSTUB_SERVER_TTL 86400 + +/*% + * Configure an apex NS with glues for a static-stub zone. + * For example, for the zone named "example.com", the following RRs will be + * added to the zone DB: + * example.com. NS example.com. + * example.com. A 192.0.2.1 + * example.com. AAAA 2001:db8::1 + */ +static isc_result_t +configure_staticstub_serveraddrs(const cfg_obj_t *zconfig, dns_zone_t *zone, + dns_rdatalist_t *rdatalist_ns, + dns_rdatalist_t *rdatalist_a, + dns_rdatalist_t *rdatalist_aaaa) { + const cfg_listelt_t *element; + isc_mem_t *mctx = dns_zone_getmctx(zone); + isc_region_t region, sregion; + dns_rdata_t *rdata; + isc_result_t result = ISC_R_SUCCESS; + + for (element = cfg_list_first(zconfig); element != NULL; + element = cfg_list_next(element)) + { + const isc_sockaddr_t *sa; + isc_netaddr_t na; + const cfg_obj_t *address = cfg_listelt_value(element); + dns_rdatalist_t *rdatalist; + + sa = cfg_obj_assockaddr(address); + if (isc_sockaddr_getport(sa) != 0) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "port is not configurable for " + "static stub server-addresses"); + return (ISC_R_FAILURE); + } + isc_netaddr_fromsockaddr(&na, sa); + if (isc_netaddr_getzone(&na) != 0) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "scoped address is not allowed " + "for static stub " + "server-addresses"); + return (ISC_R_FAILURE); + } + + switch (na.family) { + case AF_INET: + region.length = sizeof(na.type.in); + rdatalist = rdatalist_a; + break; + default: + INSIST(na.family == AF_INET6); + region.length = sizeof(na.type.in6); + rdatalist = rdatalist_aaaa; + break; + } + + rdata = isc_mem_get(mctx, sizeof(*rdata) + region.length); + region.base = (unsigned char *)(rdata + 1); + memmove(region.base, &na.type, region.length); + dns_rdata_init(rdata); + dns_rdata_fromregion(rdata, dns_zone_getclass(zone), + rdatalist->type, ®ion); + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + } + + /* + * If no address is specified (unlikely in this context, but possible), + * there's nothing to do anymore. + */ + if (ISC_LIST_EMPTY(rdatalist_a->rdata) && + ISC_LIST_EMPTY(rdatalist_aaaa->rdata)) + { + return (ISC_R_SUCCESS); + } + + /* Add to the list an apex NS with the ns name being the origin name */ + dns_name_toregion(dns_zone_getorigin(zone), &sregion); + rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length); + region.length = sregion.length; + region.base = (unsigned char *)(rdata + 1); + memmove(region.base, sregion.base, region.length); + dns_rdata_init(rdata); + dns_rdata_fromregion(rdata, dns_zone_getclass(zone), dns_rdatatype_ns, + ®ion); + ISC_LIST_APPEND(rdatalist_ns->rdata, rdata, link); + + return (result); +} + +/*% + * Configure an apex NS with an out-of-zone NS names for a static-stub zone. + * For example, for the zone named "example.com", something like the following + * RRs will be added to the zone DB: + * example.com. NS ns.example.net. + */ +static isc_result_t +configure_staticstub_servernames(const cfg_obj_t *zconfig, dns_zone_t *zone, + dns_rdatalist_t *rdatalist, + const char *zname) { + const cfg_listelt_t *element; + isc_mem_t *mctx = dns_zone_getmctx(zone); + dns_rdata_t *rdata; + isc_region_t sregion, region; + isc_result_t result = ISC_R_SUCCESS; + + for (element = cfg_list_first(zconfig); element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *obj; + const char *str; + dns_fixedname_t fixed_name; + dns_name_t *nsname; + isc_buffer_t b; + + obj = cfg_listelt_value(element); + str = cfg_obj_asstring(obj); + + nsname = dns_fixedname_initname(&fixed_name); + + isc_buffer_constinit(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(nsname, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "server-name '%s' is not a valid " + "name", + str); + return (result); + } + if (dns_name_issubdomain(nsname, dns_zone_getorigin(zone))) { + cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR, + "server-name '%s' must not be a " + "subdomain of zone name '%s'", + str, zname); + return (ISC_R_FAILURE); + } + + dns_name_toregion(nsname, &sregion); + rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length); + region.length = sregion.length; + region.base = (unsigned char *)(rdata + 1); + memmove(region.base, sregion.base, region.length); + dns_rdata_init(rdata); + dns_rdata_fromregion(rdata, dns_zone_getclass(zone), + dns_rdatatype_ns, ®ion); + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + } + + return (result); +} + +/*% + * Configure static-stub zone. + */ +static isc_result_t +configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone, + const char *zname, const char *dbtype) { + int i = 0; + const cfg_obj_t *obj; + isc_mem_t *mctx = dns_zone_getmctx(zone); + dns_db_t *db = NULL; + dns_dbversion_t *dbversion = NULL; + dns_dbnode_t *apexnode = NULL; + dns_name_t apexname; + isc_result_t result; + dns_rdataset_t rdataset; + dns_rdatalist_t rdatalist_ns, rdatalist_a, rdatalist_aaaa; + dns_rdatalist_t *rdatalists[] = { &rdatalist_ns, &rdatalist_a, + &rdatalist_aaaa, NULL }; + dns_rdata_t *rdata; + isc_region_t region; + + /* Create the DB beforehand */ + RETERR(dns_db_create(mctx, dbtype, dns_zone_getorigin(zone), + dns_dbtype_stub, dns_zone_getclass(zone), 0, NULL, + &db)); + + dns_rdataset_init(&rdataset); + + dns_rdatalist_init(&rdatalist_ns); + rdatalist_ns.rdclass = dns_zone_getclass(zone); + rdatalist_ns.type = dns_rdatatype_ns; + rdatalist_ns.ttl = STATICSTUB_SERVER_TTL; + + dns_rdatalist_init(&rdatalist_a); + rdatalist_a.rdclass = dns_zone_getclass(zone); + rdatalist_a.type = dns_rdatatype_a; + rdatalist_a.ttl = STATICSTUB_SERVER_TTL; + + dns_rdatalist_init(&rdatalist_aaaa); + rdatalist_aaaa.rdclass = dns_zone_getclass(zone); + rdatalist_aaaa.type = dns_rdatatype_aaaa; + rdatalist_aaaa.ttl = STATICSTUB_SERVER_TTL; + + /* Prepare zone RRs from the configuration */ + obj = NULL; + result = cfg_map_get(zconfig, "server-addresses", &obj); + if (result == ISC_R_SUCCESS) { + INSIST(obj != NULL); + CHECK(configure_staticstub_serveraddrs(obj, zone, &rdatalist_ns, + &rdatalist_a, + &rdatalist_aaaa)); + } + + obj = NULL; + result = cfg_map_get(zconfig, "server-names", &obj); + if (result == ISC_R_SUCCESS) { + INSIST(obj != NULL); + CHECK(configure_staticstub_servernames(obj, zone, &rdatalist_ns, + zname)); + } + + /* + * Sanity check: there should be at least one NS RR at the zone apex + * to trigger delegation. + */ + if (ISC_LIST_EMPTY(rdatalist_ns.rdata)) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "No NS record is configured for a " + "static-stub zone '%s'", + zname); + result = ISC_R_FAILURE; + goto cleanup; + } + + /* + * Now add NS and glue A/AAAA RRsets to the zone DB. + * First open a new version for the add operation and get a pointer + * to the apex node (all RRs are of the apex name). + */ + CHECK(dns_db_newversion(db, &dbversion)); + + dns_name_init(&apexname, NULL); + dns_name_clone(dns_zone_getorigin(zone), &apexname); + CHECK(dns_db_findnode(db, &apexname, false, &apexnode)); + + /* Add NS RRset */ + RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_ns, &rdataset) == + ISC_R_SUCCESS); + CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset, 0, + NULL)); + dns_rdataset_disassociate(&rdataset); + + /* Add glue A RRset, if any */ + if (!ISC_LIST_EMPTY(rdatalist_a.rdata)) { + RUNTIME_CHECK( + dns_rdatalist_tordataset(&rdatalist_a, &rdataset) == + ISC_R_SUCCESS); + CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset, + 0, NULL)); + dns_rdataset_disassociate(&rdataset); + } + + /* Add glue AAAA RRset, if any */ + if (!ISC_LIST_EMPTY(rdatalist_aaaa.rdata)) { + RUNTIME_CHECK( + dns_rdatalist_tordataset(&rdatalist_aaaa, &rdataset) == + ISC_R_SUCCESS); + CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset, + 0, NULL)); + dns_rdataset_disassociate(&rdataset); + } + + dns_db_closeversion(db, &dbversion, true); + dns_zone_setdb(zone, db); + + result = ISC_R_SUCCESS; + +cleanup: + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + } + if (apexnode != NULL) { + dns_db_detachnode(db, &apexnode); + } + if (dbversion != NULL) { + dns_db_closeversion(db, &dbversion, false); + } + if (db != NULL) { + dns_db_detach(&db); + } + for (i = 0; rdatalists[i] != NULL; i++) { + while ((rdata = ISC_LIST_HEAD(rdatalists[i]->rdata)) != NULL) { + ISC_LIST_UNLINK(rdatalists[i]->rdata, rdata, link); + dns_rdata_toregion(rdata, ®ion); + isc_mem_put(mctx, rdata, + sizeof(*rdata) + region.length); + } + } + + INSIST(dbversion == NULL); + + return (result); +} + +/*% + * Convert a config file zone type into a server zone type. + */ +static dns_zonetype_t +zonetype_fromconfig(const cfg_obj_t *map) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = cfg_map_get(map, "type", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + return (named_config_getzonetype(obj)); +} + +/*% + * Helper function for strtoargv(). Pardon the gratuitous recursion. + */ +static isc_result_t +strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp, + unsigned int n) { + isc_result_t result; + + /* Discard leading whitespace. */ + while (*s == ' ' || *s == '\t') { + s++; + } + + if (*s == '\0') { + /* We have reached the end of the string. */ + *argcp = n; + *argvp = isc_mem_get(mctx, n * sizeof(char *)); + } else { + char *p = s; + while (*p != ' ' && *p != '\t' && *p != '\0') { + p++; + } + if (*p != '\0') { + *p++ = '\0'; + } + + result = strtoargvsub(mctx, p, argcp, argvp, n + 1); + if (result != ISC_R_SUCCESS) { + return (result); + } + (*argvp)[n] = s; + } + return (ISC_R_SUCCESS); +} + +/*% + * Tokenize the string "s" into whitespace-separated words, + * return the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_put(). The string + * is modified in-place. + */ +static isc_result_t +strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) { + return (strtoargvsub(mctx, s, argcp, argvp, 0)); +} + +static const char *const primary_synonyms[] = { "primary", "master", NULL }; + +static const char *const secondary_synonyms[] = { "secondary", "slave", NULL }; + +static void +checknames(dns_zonetype_t ztype, const cfg_obj_t **maps, + const cfg_obj_t **objp) { + isc_result_t result; + + switch (ztype) { + case dns_zone_secondary: + case dns_zone_mirror: + result = named_checknames_get(maps, secondary_synonyms, objp); + break; + case dns_zone_primary: + result = named_checknames_get(maps, primary_synonyms, objp); + break; + default: + UNREACHABLE(); + } + + INSIST(result == ISC_R_SUCCESS && objp != NULL && *objp != NULL); +} + +/* + * Callback to see if a non-recursive query coming from 'srcaddr' to + * 'destaddr', with optional key 'mykey' for class 'rdclass' would be + * delivered to 'myview'. + * + * We run this unlocked as both the view list and the interface list + * are updated when the appropriate task has exclusivity. + */ +static bool +isself(dns_view_t *myview, dns_tsigkey_t *mykey, const isc_sockaddr_t *srcaddr, + const isc_sockaddr_t *dstaddr, dns_rdataclass_t rdclass, void *arg) { + dns_aclenv_t *env = NULL; + dns_view_t *view = NULL; + dns_tsigkey_t *key = NULL; + isc_netaddr_t netsrc; + isc_netaddr_t netdst; + + UNUSED(arg); + + /* interfacemgr can be destroyed only in exclusive mode. */ + if (named_g_server->interfacemgr == NULL) { + return (true); + } + + if (!ns_interfacemgr_listeningon(named_g_server->interfacemgr, dstaddr)) + { + return (false); + } + + isc_netaddr_fromsockaddr(&netsrc, srcaddr); + isc_netaddr_fromsockaddr(&netdst, dstaddr); + env = ns_interfacemgr_getaclenv(named_g_server->interfacemgr); + + for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL; + view = ISC_LIST_NEXT(view, link)) + { + const dns_name_t *tsig = NULL; + + if (view->matchrecursiveonly) { + continue; + } + + if (rdclass != view->rdclass) { + continue; + } + + if (mykey != NULL) { + bool match; + isc_result_t result; + + result = dns_view_gettsig(view, &mykey->name, &key); + if (result != ISC_R_SUCCESS) { + continue; + } + match = dst_key_compare(mykey->key, key->key); + dns_tsigkey_detach(&key); + if (!match) { + continue; + } + tsig = dns_tsigkey_identity(mykey); + } + + if (dns_acl_allowed(&netsrc, tsig, view->matchclients, env) && + dns_acl_allowed(&netdst, tsig, view->matchdestinations, + env)) + { + break; + } + } + return (view == myview); +} + +/*% + * For mirror zones, change "notify yes;" to "notify explicit;", informing the + * user only if "notify" was explicitly configured rather than inherited from + * default configuration. + */ +static dns_notifytype_t +process_notifytype(dns_notifytype_t ntype, dns_zonetype_t ztype, + const char *zname, const cfg_obj_t **maps) { + const cfg_obj_t *obj = NULL; + + /* + * Return the original setting if this is not a mirror zone or if the + * zone is configured with something else than "notify yes;". + */ + if (ztype != dns_zone_mirror || ntype != dns_notifytype_yes) { + return (ntype); + } + + /* + * Only log a message if "notify" was set in the configuration + * hierarchy supplied in 'maps'. + */ + if (named_config_get(maps, "notify", &obj) == ISC_R_SUCCESS) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO, + "'notify explicit;' will be used for mirror zone " + "'%s'", + zname); + } + + return (dns_notifytype_explicit); +} + +isc_result_t +named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, + const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac, + dns_kasplist_t *kasplist, dns_zone_t *zone, + dns_zone_t *raw) { + isc_result_t result; + const char *zname; + dns_rdataclass_t zclass; + dns_rdataclass_t vclass; + const cfg_obj_t *maps[5]; + const cfg_obj_t *nodefault[4]; + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *options = NULL; + const cfg_obj_t *obj; + const char *filename = NULL; + const char *kaspname = NULL; + const char *dupcheck; + dns_notifytype_t notifytype = dns_notifytype_yes; + uint32_t count; + unsigned int dbargc; + char **dbargv; + static char default_dbtype[] = "rbt"; + static char dlz_dbtype[] = "dlz"; + char *cpval = default_dbtype; + isc_mem_t *mctx = dns_zone_getmctx(zone); + dns_dialuptype_t dialup = dns_dialuptype_no; + dns_zonetype_t ztype; + int i; + int32_t journal_size; + bool multi; + bool alt; + dns_view_t *view = NULL; + dns_kasp_t *kasp = NULL; + bool check = false, fail = false; + bool warn = false, ignore = false; + bool ixfrdiff; + bool use_kasp = false; + dns_masterformat_t masterformat; + const dns_master_style_t *masterstyle = &dns_master_style_default; + isc_stats_t *zoneqrystats; + dns_stats_t *rcvquerystats; + dns_stats_t *dnssecsignstats; + dns_zonestat_level_t statlevel = dns_zonestat_none; + int seconds; + dns_ttl_t maxttl = 0; /* unlimited */ + dns_zone_t *mayberaw = (raw != NULL) ? raw : zone; + bool transferinsecs = ns_server_getoption(named_g_server->sctx, + NS_SERVER_TRANSFERINSECS); + + i = 0; + if (zconfig != NULL) { + zoptions = cfg_tuple_get(zconfig, "options"); + nodefault[i] = maps[i] = zoptions; + i++; + } + if (vconfig != NULL) { + nodefault[i] = maps[i] = cfg_tuple_get(vconfig, "options"); + i++; + } + if (config != NULL) { + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + nodefault[i] = maps[i] = options; + i++; + } + } + nodefault[i] = NULL; + maps[i++] = named_g_defaults; + maps[i] = NULL; + + if (vconfig != NULL) { + CHECK(named_config_getclass(cfg_tuple_get(vconfig, "class"), + dns_rdataclass_in, &vclass)); + } else { + vclass = dns_rdataclass_in; + } + + /* + * Configure values common to all zone types. + */ + + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + + CHECK(named_config_getclass(cfg_tuple_get(zconfig, "class"), vclass, + &zclass)); + dns_zone_setclass(zone, zclass); + if (raw != NULL) { + dns_zone_setclass(raw, zclass); + } + + ztype = zonetype_fromconfig(zoptions); + if (raw != NULL) { + dns_zone_settype(raw, ztype); + dns_zone_settype(zone, dns_zone_primary); + } else { + dns_zone_settype(zone, ztype); + } + + obj = NULL; + result = cfg_map_get(zoptions, "database", &obj); + if (result == ISC_R_SUCCESS) { + cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); + } + if (cpval == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + obj = NULL; + result = cfg_map_get(zoptions, "dlz", &obj); + if (result == ISC_R_SUCCESS) { + const char *dlzname = cfg_obj_asstring(obj); + size_t len; + + if (cpval != default_dbtype) { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': both 'database' and 'dlz' " + "specified", + zname); + CHECK(ISC_R_FAILURE); + } + + len = strlen(dlzname) + 5; + cpval = isc_mem_allocate(mctx, len); + snprintf(cpval, len, "dlz %s", dlzname); + } + + result = strtoargv(mctx, cpval, &dbargc, &dbargv); + if (result != ISC_R_SUCCESS && cpval != default_dbtype) { + isc_mem_free(mctx, cpval); + CHECK(result); + } + + /* + * ANSI C is strange here. There is no logical reason why (char **) + * cannot be promoted automatically to (const char * const *) by the + * compiler w/o generating a warning. + */ + dns_zone_setdbtype(zone, dbargc, (const char *const *)dbargv); + isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv)); + if (cpval != default_dbtype && cpval != dlz_dbtype) { + isc_mem_free(mctx, cpval); + } + + obj = NULL; + result = cfg_map_get(zoptions, "file", &obj); + if (result == ISC_R_SUCCESS) { + filename = cfg_obj_asstring(obj); + } + + /* + * Unless we're using some alternative database, a primary zone + * will be needing a master file. + */ + if (ztype == dns_zone_primary && cpval == default_dbtype && + filename == NULL) + { + isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, + NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, + "zone '%s': 'file' not specified", zname); + CHECK(ISC_R_FAILURE); + } + + if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) { + masterformat = dns_masterformat_raw; + } else { + masterformat = dns_masterformat_text; + } + obj = NULL; + result = named_config_get(maps, "masterfile-format", &obj); + if (result == ISC_R_SUCCESS) { + const char *masterformatstr = cfg_obj_asstring(obj); + + if (strcasecmp(masterformatstr, "text") == 0) { + masterformat = dns_masterformat_text; + } else if (strcasecmp(masterformatstr, "raw") == 0) { + masterformat = dns_masterformat_raw; + } else { + UNREACHABLE(); + } + } + + obj = NULL; + result = named_config_get(maps, "masterfile-style", &obj); + if (result == ISC_R_SUCCESS) { + const char *masterstylestr = cfg_obj_asstring(obj); + + if (masterformat != dns_masterformat_text) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "zone '%s': 'masterfile-style' " + "can only be used with " + "'masterfile-format text'", + zname); + CHECK(ISC_R_FAILURE); + } + + if (strcasecmp(masterstylestr, "full") == 0) { + masterstyle = &dns_master_style_full; + } else if (strcasecmp(masterstylestr, "relative") == 0) { + masterstyle = &dns_master_style_default; + } else { + UNREACHABLE(); + } + } + + obj = NULL; + result = named_config_get(maps, "max-records", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj)); + if (zone != mayberaw) { + dns_zone_setmaxrecords(zone, 0); + } + + if (raw != NULL && filename != NULL) { +#define SIGNED ".signed" + size_t signedlen = strlen(filename) + sizeof(SIGNED); + char *signedname; + + CHECK(dns_zone_setfile(raw, filename, masterformat, + masterstyle)); + signedname = isc_mem_get(mctx, signedlen); + + (void)snprintf(signedname, signedlen, "%s" SIGNED, filename); + result = dns_zone_setfile(zone, signedname, + dns_masterformat_raw, NULL); + isc_mem_put(mctx, signedname, signedlen); + CHECK(result); + } else { + CHECK(dns_zone_setfile(zone, filename, masterformat, + masterstyle)); + } + + obj = NULL; + result = cfg_map_get(zoptions, "journal", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(dns_zone_setjournal(mayberaw, cfg_obj_asstring(obj))); + } + + /* + * Notify messages are processed by the raw zone if it exists. + */ + if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) { + CHECK(configure_zone_acl(zconfig, vconfig, config, allow_notify, + ac, mayberaw, dns_zone_setnotifyacl, + dns_zone_clearnotifyacl)); + } + + /* + * XXXAG This probably does not make sense for stubs. + */ + CHECK(configure_zone_acl(zconfig, vconfig, config, allow_query, ac, + zone, dns_zone_setqueryacl, + dns_zone_clearqueryacl)); + + CHECK(configure_zone_acl(zconfig, vconfig, config, allow_query_on, ac, + zone, dns_zone_setqueryonacl, + dns_zone_clearqueryonacl)); + + obj = NULL; + result = named_config_get(maps, "dialup", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + dialup = dns_dialuptype_yes; + } else { + dialup = dns_dialuptype_no; + } + } else { + const char *dialupstr = cfg_obj_asstring(obj); + if (strcasecmp(dialupstr, "notify") == 0) { + dialup = dns_dialuptype_notify; + } else if (strcasecmp(dialupstr, "notify-passive") == 0) { + dialup = dns_dialuptype_notifypassive; + } else if (strcasecmp(dialupstr, "refresh") == 0) { + dialup = dns_dialuptype_refresh; + } else if (strcasecmp(dialupstr, "passive") == 0) { + dialup = dns_dialuptype_passive; + } else { + UNREACHABLE(); + } + } + if (raw != NULL) { + dns_zone_setdialup(raw, dialup); + } + dns_zone_setdialup(zone, dialup); + + obj = NULL; + result = named_config_get(maps, "zone-statistics", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + statlevel = dns_zonestat_full; + } else { + statlevel = dns_zonestat_none; + } + } else { + const char *levelstr = cfg_obj_asstring(obj); + if (strcasecmp(levelstr, "full") == 0) { + statlevel = dns_zonestat_full; + } else if (strcasecmp(levelstr, "terse") == 0) { + statlevel = dns_zonestat_terse; + } else if (strcasecmp(levelstr, "none") == 0) { + statlevel = dns_zonestat_none; + } else { + UNREACHABLE(); + } + } + dns_zone_setstatlevel(zone, statlevel); + + zoneqrystats = NULL; + rcvquerystats = NULL; + dnssecsignstats = NULL; + if (statlevel == dns_zonestat_full) { + CHECK(isc_stats_create(mctx, &zoneqrystats, + ns_statscounter_max)); + CHECK(dns_rdatatypestats_create(mctx, &rcvquerystats)); + CHECK(dns_dnssecsignstats_create(mctx, &dnssecsignstats)); + } + dns_zone_setrequeststats(zone, zoneqrystats); + dns_zone_setrcvquerystats(zone, rcvquerystats); + dns_zone_setdnssecsignstats(zone, dnssecsignstats); + + if (zoneqrystats != NULL) { + isc_stats_detach(&zoneqrystats); + } + + if (rcvquerystats != NULL) { + dns_stats_detach(&rcvquerystats); + } + + if (dnssecsignstats != NULL) { + dns_stats_detach(&dnssecsignstats); + } + + /* + * Configure authoritative zone functionality. This applies + * to primary servers (type "primary") and secondaries + * acting as primaries (type "secondary"), but not to stubs. + */ + if (ztype != dns_zone_stub && ztype != dns_zone_staticstub && + ztype != dns_zone_redirect) + { + obj = NULL; + result = named_config_get(maps, "dnssec-policy", &obj); + if (result == ISC_R_SUCCESS) { + kaspname = cfg_obj_asstring(obj); + if (strcmp(kaspname, "none") != 0) { + result = dns_kasplist_find(kasplist, kaspname, + &kasp); + if (result != ISC_R_SUCCESS) { + cfg_obj_log( + obj, named_g_lctx, + ISC_LOG_ERROR, + "dnssec-policy '%s' not found ", + kaspname); + CHECK(result); + } + dns_zone_setkasp(zone, kasp); + use_kasp = true; + } + } + if (!use_kasp) { + dns_zone_setkasp(zone, NULL); + } + + obj = NULL; + result = named_config_get(maps, "notify", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + notifytype = dns_notifytype_yes; + } else { + notifytype = dns_notifytype_no; + } + } else { + const char *str = cfg_obj_asstring(obj); + if (strcasecmp(str, "explicit") == 0) { + notifytype = dns_notifytype_explicit; + } else if (strcasecmp(str, "master-only") == 0 || + strcasecmp(str, "primary-only") == 0) + { + notifytype = dns_notifytype_masteronly; + } else { + UNREACHABLE(); + } + } + notifytype = process_notifytype(notifytype, ztype, zname, + nodefault); + if (raw != NULL) { + dns_zone_setnotifytype(raw, dns_notifytype_no); + } + dns_zone_setnotifytype(zone, notifytype); + + obj = NULL; + result = named_config_get(maps, "also-notify", &obj); + if (result == ISC_R_SUCCESS && + (notifytype == dns_notifytype_yes || + notifytype == dns_notifytype_explicit || + (notifytype == dns_notifytype_masteronly && + ztype == dns_zone_primary))) + { + dns_ipkeylist_t ipkl; + dns_ipkeylist_init(&ipkl); + + CHECK(named_config_getipandkeylist(config, "primaries", + obj, mctx, &ipkl)); + dns_zone_setalsonotify(zone, ipkl.addrs, ipkl.keys, + ipkl.tlss, ipkl.count); + dns_ipkeylist_clear(mctx, &ipkl); + } else { + dns_zone_setalsonotify(zone, NULL, NULL, NULL, 0); + } + + obj = NULL; + result = named_config_get(maps, "parental-source", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + + CHECK(dns_zone_setparentalsrc4(zone, cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "parental-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + + CHECK(dns_zone_setparentalsrc6(zone, cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "notify-source", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "notify-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "notify-to-soa", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_NOTIFYTOSOA, + cfg_obj_asboolean(obj)); + + dns_zone_setisself(zone, isself, NULL); + + CHECK(configure_zone_acl( + zconfig, vconfig, config, allow_transfer, ac, zone, + dns_zone_setxfracl, dns_zone_clearxfracl)); + + obj = NULL; + result = named_config_get(maps, "max-transfer-time-out", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxxfrout( + zone, transferinsecs ? cfg_obj_asuint32(obj) + : cfg_obj_asuint32(obj) * 60); + + obj = NULL; + result = named_config_get(maps, "max-transfer-idle-out", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setidleout(zone, transferinsecs + ? cfg_obj_asuint32(obj) + : cfg_obj_asuint32(obj) * 60); + + obj = NULL; + result = named_config_get(maps, "max-journal-size", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (raw != NULL) { + dns_zone_setjournalsize(raw, -1); + } + dns_zone_setjournalsize(zone, -1); + if (cfg_obj_isstring(obj)) { + const char *str = cfg_obj_asstring(obj); + if (strcasecmp(str, "unlimited") == 0) { + journal_size = DNS_JOURNAL_SIZE_MAX; + } else { + INSIST(strcasecmp(str, "default") == 0); + journal_size = -1; + } + } else { + isc_resourcevalue_t value; + value = cfg_obj_asuint64(obj); + if (value > DNS_JOURNAL_SIZE_MAX) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "'max-journal-size " + "%" PRId64 "' " + "is too large", + value); + CHECK(ISC_R_RANGE); + } + journal_size = (uint32_t)value; + } + if (raw != NULL) { + dns_zone_setjournalsize(raw, journal_size); + } + dns_zone_setjournalsize(zone, journal_size); + + obj = NULL; + result = named_config_get(maps, "ixfr-from-differences", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isboolean(obj)) { + ixfrdiff = cfg_obj_asboolean(obj); + } else if ((strcasecmp(cfg_obj_asstring(obj), "primary") == 0 || + strcasecmp(cfg_obj_asstring(obj), "master") == 0) && + ztype == dns_zone_primary) + { + ixfrdiff = true; + } else if ((strcasecmp(cfg_obj_asstring(obj), "secondary") == + 0 || + strcasecmp(cfg_obj_asstring(obj), "slave") == 0) && + ztype == dns_zone_secondary) + { + ixfrdiff = true; + } else { + ixfrdiff = false; + } + if (raw != NULL) { + dns_zone_setoption(raw, DNS_ZONEOPT_IXFRFROMDIFFS, + true); + dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, + false); + } else { + dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, + ixfrdiff); + } + + obj = NULL; + result = named_config_get(maps, "max-ixfr-ratio", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (cfg_obj_isstring(obj)) { + dns_zone_setixfrratio(zone, 0); + } else { + dns_zone_setixfrratio(zone, cfg_obj_aspercentage(obj)); + } + + obj = NULL; + result = named_config_get(maps, "request-expire", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zone_setrequestexpire(zone, cfg_obj_asboolean(obj)); + + obj = NULL; + result = named_config_get(maps, "request-ixfr", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zone_setrequestixfr(zone, cfg_obj_asboolean(obj)); + + obj = NULL; + checknames(ztype, maps, &obj); + INSIST(obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + fail = false; + check = true; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + fail = check = true; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + fail = check = false; + } else { + UNREACHABLE(); + } + if (raw != NULL) { + dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMES, check); + dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMESFAIL, + fail); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, false); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, + false); + } else { + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, + fail); + } + + obj = NULL; + result = named_config_get(maps, "notify-delay", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "check-sibling", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSIBLING, + cfg_obj_asboolean(obj)); + + obj = NULL; + result = named_config_get(maps, "check-spf", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + check = true; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + check = false; + } else { + UNREACHABLE(); + } + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSPF, check); + + obj = NULL; + result = named_config_get(maps, "zero-no-soa-ttl", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj)); + + obj = NULL; + result = named_config_get(maps, "nsec3-test-zone", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE, + cfg_obj_asboolean(obj)); + } else if (ztype == dns_zone_redirect) { + dns_zone_setnotifytype(zone, dns_notifytype_no); + + obj = NULL; + result = named_config_get(maps, "max-journal-size", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setjournalsize(zone, -1); + if (cfg_obj_isstring(obj)) { + const char *str = cfg_obj_asstring(obj); + if (strcasecmp(str, "unlimited") == 0) { + journal_size = DNS_JOURNAL_SIZE_MAX; + } else { + INSIST(strcasecmp(str, "default") == 0); + journal_size = -1; + } + } else { + isc_resourcevalue_t value; + value = cfg_obj_asuint64(obj); + if (value > DNS_JOURNAL_SIZE_MAX) { + cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR, + "'max-journal-size " + "%" PRId64 "' " + "is too large", + value); + CHECK(ISC_R_RANGE); + } + journal_size = (uint32_t)value; + } + dns_zone_setjournalsize(zone, journal_size); + } + + if (use_kasp) { + maxttl = dns_kasp_zonemaxttl(dns_zone_getkasp(zone), false); + } else { + obj = NULL; + result = named_config_get(maps, "max-zone-ttl", &obj); + if (result == ISC_R_SUCCESS) { + if (cfg_obj_isduration(obj)) { + maxttl = cfg_obj_asduration(obj); + } + } + } + dns_zone_setmaxttl(zone, maxttl); + if (raw != NULL) { + dns_zone_setmaxttl(raw, maxttl); + } + + /* + * Configure update-related options. These apply to + * primary servers only. + */ + if (ztype == dns_zone_primary) { + dns_acl_t *updateacl; + + CHECK(configure_zone_acl(zconfig, vconfig, config, allow_update, + ac, mayberaw, dns_zone_setupdateacl, + dns_zone_clearupdateacl)); + + updateacl = dns_zone_getupdateacl(mayberaw); + if (updateacl != NULL && dns_acl_isinsecure(updateacl)) { + isc_log_write(named_g_lctx, DNS_LOGCATEGORY_SECURITY, + NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING, + "zone '%s' allows unsigned updates " + "from remote hosts, which is insecure", + zname); + } + + CHECK(configure_zone_ssutable(zoptions, mayberaw, zname)); + } + + /* + * Configure DNSSEC signing. These apply to primary zones or zones that + * use inline-signing (raw != NULL). + */ + if (ztype == dns_zone_primary || raw != NULL) { + const cfg_obj_t *validity, *resign; + bool allow = false, maint = false; + bool sigvalinsecs; + + if (use_kasp) { + if (dns_kasp_nsec3(kasp)) { + result = dns_zone_setnsec3param( + zone, 1, dns_kasp_nsec3flags(kasp), + dns_kasp_nsec3iter(kasp), + dns_kasp_nsec3saltlen(kasp), NULL, true, + false); + } else { + result = dns_zone_setnsec3param( + zone, 0, 0, 0, 0, NULL, true, false); + } + INSIST(result == ISC_R_SUCCESS); + } + + if (use_kasp) { + seconds = (uint32_t)dns_kasp_sigvalidity_dnskey(kasp); + } else { + obj = NULL; + result = named_config_get(maps, "dnskey-sig-validity", + &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + seconds = cfg_obj_asuint32(obj) * 86400; + } + dns_zone_setkeyvalidityinterval(zone, seconds); + + if (use_kasp) { + seconds = (uint32_t)dns_kasp_sigvalidity(kasp); + dns_zone_setsigvalidityinterval(zone, seconds); + seconds = (uint32_t)dns_kasp_sigrefresh(kasp); + dns_zone_setsigresigninginterval(zone, seconds); + } else { + obj = NULL; + result = named_config_get(maps, "sig-validity-interval", + &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + + sigvalinsecs = ns_server_getoption( + named_g_server->sctx, NS_SERVER_SIGVALINSECS); + validity = cfg_tuple_get(obj, "validity"); + seconds = cfg_obj_asuint32(validity); + if (!sigvalinsecs) { + seconds *= 86400; + } + dns_zone_setsigvalidityinterval(zone, seconds); + + resign = cfg_tuple_get(obj, "re-sign"); + if (cfg_obj_isvoid(resign)) { + seconds /= 4; + } else if (!sigvalinsecs) { + uint32_t r = cfg_obj_asuint32(resign); + if (seconds > 7 * 86400) { + seconds = r * 86400; + } else { + seconds = r * 3600; + } + } else { + seconds = cfg_obj_asuint32(resign); + } + dns_zone_setsigresigninginterval(zone, seconds); + } + + obj = NULL; + result = named_config_get(maps, "key-directory", &obj); + if (result == ISC_R_SUCCESS) { + filename = cfg_obj_asstring(obj); + CHECK(dns_zone_setkeydirectory(zone, filename)); + } + + obj = NULL; + result = named_config_get(maps, "sig-signing-signatures", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setsignatures(zone, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "sig-signing-nodes", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setnodes(zone, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "sig-signing-type", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setprivatetype(zone, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "update-check-ksk", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_UPDATECHECKKSK, + cfg_obj_asboolean(obj)); + /* + * This setting will be ignored if dnssec-policy is used. + * named-checkconf will error if both are configured. + */ + + obj = NULL; + result = named_config_get(maps, "dnssec-dnskey-kskonly", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_DNSKEYKSKONLY, + cfg_obj_asboolean(obj)); + /* + * This setting will be ignored if dnssec-policy is used. + * named-checkconf will error if both are configured. + */ + + obj = NULL; + result = named_config_get(maps, "dnssec-loadkeys-interval", + &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setrefreshkeyinterval(zone, + cfg_obj_asuint32(obj))); + + obj = NULL; + result = cfg_map_get(zoptions, "auto-dnssec", &obj); + if (kasp != NULL) { + bool s2i = (strcmp(dns_kasp_getname(kasp), + "insecure") != 0); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, true); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, !s2i); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, true); + } else if (result == ISC_R_SUCCESS) { + const char *arg = cfg_obj_asstring(obj); + if (strcasecmp(arg, "allow") == 0) { + allow = true; + } else if (strcasecmp(arg, "maintain") == 0) { + allow = maint = true; + } else if (strcasecmp(arg, "off") == 0) { + /* Default */ + } else { + UNREACHABLE(); + } + dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_CREATE, false); + dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint); + } + } + + if (ztype == dns_zone_secondary || ztype == dns_zone_mirror) { + CHECK(configure_zone_acl(zconfig, vconfig, config, + allow_update_forwarding, ac, mayberaw, + dns_zone_setforwardacl, + dns_zone_clearforwardacl)); + } + + /*% + * Configure parental agents, applies to primary and secondary zones. + */ + if (ztype == dns_zone_primary || ztype == dns_zone_secondary) { + obj = NULL; + (void)cfg_map_get(zoptions, "parental-agents", &obj); + if (obj != NULL) { + dns_ipkeylist_t ipkl; + dns_ipkeylist_init(&ipkl); + CHECK(named_config_getipandkeylist( + config, "parental-agents", obj, mctx, &ipkl)); + dns_zone_setparentals(zone, ipkl.addrs, ipkl.keys, + ipkl.tlss, ipkl.count); + dns_ipkeylist_clear(mctx, &ipkl); + } else { + dns_zone_setparentals(zone, NULL, NULL, NULL, 0); + } + } + + /*% + * Configure primary zone functionality. + */ + if (ztype == dns_zone_primary) { + obj = NULL; + result = named_config_get(maps, "check-wildcard", &obj); + if (result == ISC_R_SUCCESS) { + check = cfg_obj_asboolean(obj); + } else { + check = false; + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKWILDCARD, check); + + obj = NULL; + result = named_config_get(maps, "check-dup-records", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dupcheck = cfg_obj_asstring(obj); + if (strcasecmp(dupcheck, "warn") == 0) { + fail = false; + check = true; + } else if (strcasecmp(dupcheck, "fail") == 0) { + fail = check = true; + } else if (strcasecmp(dupcheck, "ignore") == 0) { + fail = check = false; + } else { + UNREACHABLE(); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRR, check); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRRFAIL, fail); + + obj = NULL; + result = named_config_get(maps, "check-mx", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + fail = false; + check = true; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + fail = check = true; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + fail = check = false; + } else { + UNREACHABLE(); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMX, check); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMXFAIL, fail); + + obj = NULL; + result = named_config_get(maps, "check-integrity", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKINTEGRITY, + cfg_obj_asboolean(obj)); + + obj = NULL; + result = named_config_get(maps, "check-mx-cname", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + warn = true; + ignore = false; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + warn = ignore = false; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + warn = ignore = true; + } else { + UNREACHABLE(); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNMXCNAME, warn); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNOREMXCNAME, ignore); + + obj = NULL; + result = named_config_get(maps, "check-srv-cname", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + warn = true; + ignore = false; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + warn = ignore = false; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + warn = ignore = true; + } else { + UNREACHABLE(); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNSRVCNAME, warn); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNORESRVCNAME, + ignore); + + obj = NULL; + result = named_config_get(maps, "dnssec-secure-to-insecure", + &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_SECURETOINSECURE, + cfg_obj_asboolean(obj)); + + obj = NULL; + result = cfg_map_get(zoptions, "dnssec-update-mode", &obj); + if (result == ISC_R_SUCCESS) { + const char *arg = cfg_obj_asstring(obj); + if (strcasecmp(arg, "no-resign") == 0) { + dns_zone_setkeyopt(zone, DNS_ZONEKEY_NORESIGN, + true); + } else if (strcasecmp(arg, "maintain") == 0) { + /* Default */ + } else { + UNREACHABLE(); + } + } + + obj = NULL; + result = named_config_get(maps, "serial-update-method", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "unixtime") == 0) { + dns_zone_setserialupdatemethod( + zone, dns_updatemethod_unixtime); + } else if (strcasecmp(cfg_obj_asstring(obj), "date") == 0) { + dns_zone_setserialupdatemethod(zone, + dns_updatemethod_date); + } else { + dns_zone_setserialupdatemethod( + zone, dns_updatemethod_increment); + } + } + + /* + * Configure secondary zone functionality. + */ + switch (ztype) { + case dns_zone_mirror: + /* + * Disable outgoing zone transfers for mirror zones unless they + * are explicitly enabled by zone configuration. + */ + obj = NULL; + (void)cfg_map_get(zoptions, "allow-transfer", &obj); + if (obj == NULL) { + dns_acl_t *none; + CHECK(dns_acl_none(mctx, &none)); + dns_zone_setxfracl(zone, none); + dns_acl_detach(&none); + } + FALLTHROUGH; + case dns_zone_secondary: + case dns_zone_stub: + case dns_zone_redirect: + count = 0; + obj = NULL; + (void)cfg_map_get(zoptions, "primaries", &obj); + if (obj == NULL) { + (void)cfg_map_get(zoptions, "masters", &obj); + } + + /* + * Use the built-in primary server list if one was not + * explicitly specified and this is a root zone mirror. + */ + if (obj == NULL && ztype == dns_zone_mirror && + dns_name_equal(dns_zone_getorigin(zone), dns_rootname)) + { + result = named_config_getremotesdef( + named_g_config, "primaries", + DEFAULT_IANA_ROOT_ZONE_PRIMARIES, &obj); + CHECK(result); + } + if (obj != NULL) { + dns_ipkeylist_t ipkl; + dns_ipkeylist_init(&ipkl); + + CHECK(named_config_getipandkeylist(config, "primaries", + obj, mctx, &ipkl)); + dns_zone_setprimaries(mayberaw, ipkl.addrs, ipkl.keys, + ipkl.tlss, ipkl.count); + count = ipkl.count; + dns_ipkeylist_clear(mctx, &ipkl); + } else { + dns_zone_setprimaries(mayberaw, NULL, NULL, NULL, 0); + } + + multi = false; + if (count > 1) { + obj = NULL; + result = named_config_get(maps, "multi-master", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + multi = cfg_obj_asboolean(obj); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_MULTIMASTER, multi); + + obj = NULL; + result = named_config_get(maps, "max-transfer-time-in", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxxfrin( + mayberaw, transferinsecs ? cfg_obj_asuint32(obj) + : cfg_obj_asuint32(obj) * 60); + + obj = NULL; + result = named_config_get(maps, "max-transfer-idle-in", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setidlein(mayberaw, + transferinsecs ? cfg_obj_asuint32(obj) + : cfg_obj_asuint32(obj) * 60); + + obj = NULL; + result = named_config_get(maps, "max-refresh-time", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxrefreshtime(mayberaw, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "min-refresh-time", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setminrefreshtime(mayberaw, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "max-retry-time", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxretrytime(mayberaw, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "min-retry-time", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setminretrytime(mayberaw, cfg_obj_asuint32(obj)); + + obj = NULL; + result = named_config_get(maps, "transfer-source", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setxfrsource4(mayberaw, + cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "transfer-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setxfrsource6(mayberaw, + cfg_obj_assockaddr(obj))); + named_add_reserved_dispatch(named_g_server, + cfg_obj_assockaddr(obj)); + + obj = NULL; + result = named_config_get(maps, "alt-transfer-source", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setaltxfrsource4(mayberaw, + cfg_obj_assockaddr(obj))); + obj = NULL; + result = named_config_get(maps, "alt-transfer-source-v6", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + CHECK(dns_zone_setaltxfrsource6(mayberaw, + cfg_obj_assockaddr(obj))); + obj = NULL; + (void)named_config_get(maps, "use-alt-transfer-source", &obj); + if (obj == NULL) { + /* + * Default off when views are in use otherwise + * on for BIND 8 compatibility. + */ + view = dns_zone_getview(zone); + if (view != NULL && strcmp(view->name, "_default") == 0) + { + alt = true; + } else { + alt = false; + } + } else { + alt = cfg_obj_asboolean(obj); + } + dns_zone_setoption(mayberaw, DNS_ZONEOPT_USEALTXFRSRC, alt); + + obj = NULL; + (void)named_config_get(maps, "try-tcp-refresh", &obj); + dns_zone_setoption(mayberaw, DNS_ZONEOPT_TRYTCPREFRESH, + cfg_obj_asboolean(obj)); + break; + + case dns_zone_staticstub: + CHECK(configure_staticstub(zoptions, zone, zname, + default_dbtype)); + break; + + default: + break; + } + + result = ISC_R_SUCCESS; + +cleanup: + if (kasp != NULL) { + dns_kasp_detach(&kasp); + } + return (result); +} + +/* + * Set up a DLZ zone as writeable + */ +isc_result_t +named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone, + dns_rdataclass_t rdclass, dns_name_t *name) { + dns_db_t *db = NULL; + isc_time_t now; + isc_result_t result; + + TIME_NOW(&now); + + dns_zone_settype(zone, dns_zone_dlz); + result = dns_sdlz_setdb(dlzdatabase, rdclass, name, &db); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = dns_zone_dlzpostload(zone, db); + dns_db_detach(&db); + return (result); +} + +bool +named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) { + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *obj = NULL; + const char *cfilename; + const char *zfilename; + dns_zone_t *raw = NULL; + bool has_raw, inline_signing; + dns_zonetype_t ztype; + + zoptions = cfg_tuple_get(zconfig, "options"); + + /* + * We always reconfigure a static-stub zone for simplicity, assuming + * the amount of data to be loaded is small. + */ + if (zonetype_fromconfig(zoptions) == dns_zone_staticstub) { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "not reusable: staticstub"); + return (false); + } + + /* If there's a raw zone, use that for filename and type comparison */ + dns_zone_getraw(zone, &raw); + if (raw != NULL) { + zfilename = dns_zone_getfile(raw); + ztype = dns_zone_gettype(raw); + dns_zone_detach(&raw); + has_raw = true; + } else { + zfilename = dns_zone_getfile(zone); + ztype = dns_zone_gettype(zone); + has_raw = false; + } + + inline_signing = named_zone_inlinesigning(zconfig); + if (!inline_signing && has_raw) { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "not reusable: old zone was inline-signing"); + return (false); + } else if (inline_signing && !has_raw) { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "not reusable: old zone was not inline-signing"); + return (false); + } + + if (zonetype_fromconfig(zoptions) != ztype) { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "not reusable: type mismatch"); + return (false); + } + + obj = NULL; + (void)cfg_map_get(zoptions, "file", &obj); + if (obj != NULL) { + cfilename = cfg_obj_asstring(obj); + } else { + cfilename = NULL; + } + if (!((cfilename == NULL && zfilename == NULL) || + (cfilename != NULL && zfilename != NULL && + strcmp(cfilename, zfilename) == 0))) + { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "not reusable: filename mismatch"); + return (false); + } + + return (true); +} + +bool +named_zone_inlinesigning(const cfg_obj_t *zconfig) { + const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *signing = NULL; + bool inline_signing = false; + + zoptions = cfg_tuple_get(zconfig, "options"); + inline_signing = (cfg_map_get(zoptions, "inline-signing", &signing) == + ISC_R_SUCCESS && + cfg_obj_asboolean(signing)); + + return (inline_signing); +} diff --git a/bin/nsupdate/Makefile.am b/bin/nsupdate/Makefile.am new file mode 100644 index 0000000..18c8645 --- /dev/null +++ b/bin/nsupdate/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBIRS_CFLAGS) \ + $(LIBBIND9_CFLAGS) \ + $(GSSAPI_CFLAGS) \ + $(KRB5_CFLAGS) \ + $(READLINE_CFLAGS) + +AM_CPPFLAGS += \ + -DSESSION_KEYFILE=\"${localstatedir}/run/named/session.key\" + +LDADD += \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBIRS_LIBS) \ + $(LIBBIND9_LIBS) \ + $(GSSAPI_LIBS) \ + $(KRB5_LIBS) + +if HAVE_READLINE +LDADD += \ + $(READLINE_LIBS) +endif + +bin_PROGRAMS = nsupdate + +nsupdate_SOURCES = \ + nsupdate.c \ + ../dig/readline.h diff --git a/bin/nsupdate/Makefile.in b/bin/nsupdate/Makefile.in new file mode 100644 index 0000000..ce9064d --- /dev/null +++ b/bin/nsupdate/Makefile.in @@ -0,0 +1,828 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +@HAVE_READLINE_TRUE@am__append_2 = \ +@HAVE_READLINE_TRUE@ $(READLINE_LIBS) + +bin_PROGRAMS = nsupdate$(EXEEXT) +subdir = bin/nsupdate +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_nsupdate_OBJECTS = nsupdate.$(OBJEXT) +nsupdate_OBJECTS = $(am_nsupdate_OBJECTS) +nsupdate_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +@HAVE_READLINE_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +nsupdate_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBIRS_LIBS) $(LIBBIND9_LIBS) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/nsupdate.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(nsupdate_SOURCES) +DIST_SOURCES = $(nsupdate_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) $(LIBIRS_CFLAGS) $(LIBBIND9_CFLAGS) \ + $(GSSAPI_CFLAGS) $(KRB5_CFLAGS) $(READLINE_CFLAGS) \ + -DSESSION_KEYFILE=\"${localstatedir}/run/named/session.key\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBISCCFG_LIBS) $(LIBIRS_LIBS) \ + $(LIBBIND9_LIBS) $(GSSAPI_LIBS) $(KRB5_LIBS) $(am__append_2) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +nsupdate_SOURCES = \ + nsupdate.c \ + ../dig/readline.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/nsupdate/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/nsupdate/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +nsupdate$(EXEEXT): $(nsupdate_OBJECTS) $(nsupdate_DEPENDENCIES) $(EXTRA_nsupdate_DEPENDENCIES) + @rm -f nsupdate$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nsupdate_OBJECTS) $(nsupdate_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsupdate.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/nsupdate.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/nsupdate.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-binPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir doc-am doc-local dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-binPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c new file mode 100644 index 0000000..ae759be --- /dev/null +++ b/bin/nsupdate/nsupdate.c @@ -0,0 +1,3463 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#if HAVE_GSSAPI +#include + +#if HAVE_KRB5_KRB5_H +#include +#elif HAVE_KRB5_H +#include +#endif + +#if HAVE_GSSAPI_GSSAPI_H +#include +#elif HAVE_GSSAPI_H +#include +#endif + +#endif /* HAVE_GSSAPI */ + +#include + +#include "../dig/readline.h" + +#define MAXCMD (128 * 1024) +#define MAXWIRE (64 * 1024) +#define INITTEXT (2 * 1024) +#define MAXTEXT (128 * 1024) +#define TTL_MAX 2147483647U /* Maximum signed 32 bit integer. */ + +#define DNSDEFAULTPORT 53 + +/* Number of addresses to request from bind9_getaddresses() */ +#define MAX_SERVERADDRS 4 + +static uint16_t dnsport = DNSDEFAULTPORT; + +#ifndef RESOLV_CONF +#define RESOLV_CONF "/etc/resolv.conf" +#endif /* ifndef RESOLV_CONF */ + +static bool debugging = false, ddebugging = false; +static bool memdebugging = false; +static bool have_ipv4 = false; +static bool have_ipv6 = false; +static bool is_dst_up = false; +static bool usevc = false; +static bool usegsstsig = false; +static bool use_win2k_gsstsig = false; +static bool tried_other_gsstsig = false; +static bool local_only = false; +static isc_nm_t *netmgr = NULL; +static isc_taskmgr_t *taskmgr = NULL; +static isc_task_t *global_task = NULL; +static isc_event_t *global_event = NULL; +static isc_log_t *glctx = NULL; +static isc_mem_t *gmctx = NULL; +static dns_dispatchmgr_t *dispatchmgr = NULL; +static dns_requestmgr_t *requestmgr = NULL; +static dns_dispatch_t *dispatchv4 = NULL; +static dns_dispatch_t *dispatchv6 = NULL; +static dns_message_t *updatemsg = NULL; +static dns_fixedname_t fuserzone; +static dns_fixedname_t fzname; +static dns_name_t *userzone = NULL; +static dns_name_t *zname = NULL; +static dns_name_t tmpzonename = DNS_NAME_INITEMPTY; +static dns_name_t restart_primary = DNS_NAME_INITEMPTY; +static dns_tsig_keyring_t *gssring = NULL; +static dns_tsigkey_t *tsigkey = NULL; +static dst_key_t *sig0key = NULL; +static isc_sockaddr_t *servers = NULL; +static isc_sockaddr_t *primary_servers = NULL; +static bool default_servers = true; +static int ns_inuse = 0; +static int primary_inuse = 0; +static int ns_total = 0; +static int ns_alloc = 0; +static int primary_total = 0; +static int primary_alloc = 0; +static isc_sockaddr_t *localaddr4 = NULL; +static isc_sockaddr_t *localaddr6 = NULL; +static const char *keyfile = NULL; +static char *keystr = NULL; +static bool shuttingdown = false; +static FILE *input; +static bool interactive = true; +static bool seenerror = false; +static const dns_master_style_t *style; +static int requests = 0; +static unsigned int logdebuglevel = 0; +static unsigned int timeout = 300; +static unsigned int udp_timeout = 3; +static unsigned int udp_retries = 3; +static dns_rdataclass_t defaultclass = dns_rdataclass_in; +static dns_rdataclass_t zoneclass = dns_rdataclass_none; +static isc_mutex_t answer_lock; +static dns_message_t *answer = NULL; +static uint32_t default_ttl = 0; +static bool default_ttl_set = false; +static bool checknames = true; +static const char *resolvconf = RESOLV_CONF; + +typedef struct nsu_requestinfo { + dns_message_t *msg; + isc_sockaddr_t *addr; +} nsu_requestinfo_t; + +static void +sendrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request); +static void +send_update(dns_name_t *zonename, isc_sockaddr_t *primary); + +noreturn static void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +static void +debug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +static void +ddebug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +#if HAVE_GSSAPI +static dns_fixedname_t fkname; +static isc_sockaddr_t *kserver = NULL; +static char *realm = NULL; +static char servicename[DNS_NAME_FORMATSIZE]; +static dns_name_t *keyname; +typedef struct nsu_gssinfo { + dns_message_t *msg; + isc_sockaddr_t *addr; + gss_ctx_id_t context; +} nsu_gssinfo_t; + +static void +failed_gssrequest(void); +static void +start_gssrequest(dns_name_t *primary); +static void +send_gssrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request, gss_ctx_id_t context); +static void +recvgss(isc_task_t *task, isc_event_t *event); +#endif /* HAVE_GSSAPI */ + +static void +error(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +#define STATUS_MORE (uint16_t)0 +#define STATUS_SEND (uint16_t)1 +#define STATUS_QUIT (uint16_t)2 +#define STATUS_SYNTAX (uint16_t)3 + +static void +primary_from_servers(void) { + if (primary_servers != NULL && primary_servers != servers) { + isc_mem_put(gmctx, primary_servers, + primary_alloc * sizeof(isc_sockaddr_t)); + } + primary_servers = servers; + primary_total = ns_total; + primary_alloc = ns_alloc; + primary_inuse = ns_inuse; +} + +static dns_rdataclass_t +getzoneclass(void) { + if (zoneclass == dns_rdataclass_none) { + zoneclass = defaultclass; + } + return (zoneclass); +} + +static bool +setzoneclass(dns_rdataclass_t rdclass) { + if (zoneclass == dns_rdataclass_none || rdclass == dns_rdataclass_none) + { + zoneclass = rdclass; + } + if (zoneclass != rdclass) { + return (false); + } + return (true); +} + +static void +fatal(const char *format, ...) { + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + +static void +error(const char *format, ...) { + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + +static void +debug(const char *format, ...) { + va_list args; + + if (debugging) { + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +static void +ddebug(const char *format, ...) { + va_list args; + + if (ddebugging) { + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +static void +check_result(isc_result_t result, const char *msg) { + if (result != ISC_R_SUCCESS) { + fatal("%s: %s", msg, isc_result_totext(result)); + } +} + +static char * +nsu_strsep(char **stringp, const char *delim) { + char *string = *stringp; + *stringp = NULL; + char *s; + const char *d; + char sc, dc; + + if (string == NULL) { + return (NULL); + } + + for (; *string != '\0'; string++) { + sc = *string; + for (d = delim; (dc = *d) != '\0'; d++) { + if (sc == dc) { + break; + } + } + if (dc == 0) { + break; + } + } + + for (s = string; *s != '\0'; s++) { + sc = *s; + for (d = delim; (dc = *d) != '\0'; d++) { + if (sc == dc) { + *s++ = '\0'; + *stringp = s; + return (string); + } + } + } + return (string); +} + +static void +reset_system(void) { + ddebug("reset_system()"); + /* If the update message is still around, destroy it */ + if (updatemsg != NULL) { + dns_message_reset(updatemsg, DNS_MESSAGE_INTENTRENDER); + } else { + dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &updatemsg); + } + updatemsg->opcode = dns_opcode_update; + if (usegsstsig) { + if (tsigkey != NULL) { + dns_tsigkey_detach(&tsigkey); + } + if (gssring != NULL) { + dns_tsigkeyring_detach(&gssring); + } + tried_other_gsstsig = false; + } +} + +static bool +parse_hmac(const dns_name_t **hmac, const char *hmacstr, size_t len, + uint16_t *digestbitsp) { + uint16_t digestbits = 0; + isc_result_t result; + char buf[20]; + + REQUIRE(hmac != NULL && *hmac == NULL); + REQUIRE(hmacstr != NULL); + + if (len >= sizeof(buf)) { + error("unknown key type '%.*s'", (int)(len), hmacstr); + return (false); + } + + /* Copy len bytes and NUL terminate. */ + strlcpy(buf, hmacstr, ISC_MIN(len + 1, sizeof(buf))); + + if (strcasecmp(buf, "hmac-md5") == 0) { + *hmac = DNS_TSIG_HMACMD5_NAME; + } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { + *hmac = DNS_TSIG_HMACMD5_NAME; + result = isc_parse_uint16(&digestbits, &buf[9], 10); + if (result != ISC_R_SUCCESS || digestbits > 128) { + error("digest-bits out of range [0..128]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha1") == 0) { + *hmac = DNS_TSIG_HMACSHA1_NAME; + } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { + *hmac = DNS_TSIG_HMACSHA1_NAME; + result = isc_parse_uint16(&digestbits, &buf[10], 10); + if (result != ISC_R_SUCCESS || digestbits > 160) { + error("digest-bits out of range [0..160]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha224") == 0) { + *hmac = DNS_TSIG_HMACSHA224_NAME; + } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA224_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 224) { + error("digest-bits out of range [0..224]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha256") == 0) { + *hmac = DNS_TSIG_HMACSHA256_NAME; + } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA256_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 256) { + error("digest-bits out of range [0..256]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha384") == 0) { + *hmac = DNS_TSIG_HMACSHA384_NAME; + } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA384_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 384) { + error("digest-bits out of range [0..384]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else if (strcasecmp(buf, "hmac-sha512") == 0) { + *hmac = DNS_TSIG_HMACSHA512_NAME; + } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { + *hmac = DNS_TSIG_HMACSHA512_NAME; + result = isc_parse_uint16(&digestbits, &buf[12], 10); + if (result != ISC_R_SUCCESS || digestbits > 512) { + error("digest-bits out of range [0..512]"); + return (false); + } + *digestbitsp = (digestbits + 7) & ~0x7U; + } else { + error("unknown key type '%s'", buf); + return (false); + } + return (true); +} + +static int +basenamelen(const char *file) { + int len = strlen(file); + + if (len > 1 && file[len - 1] == '.') { + len -= 1; + } else if (len > 8 && strcmp(file + len - 8, ".private") == 0) { + len -= 8; + } else if (len > 4 && strcmp(file + len - 4, ".key") == 0) { + len -= 4; + } + return (len); +} + +static void +setup_keystr(void) { + unsigned char *secret = NULL; + int secretlen; + isc_buffer_t secretbuf; + isc_result_t result; + isc_buffer_t keynamesrc; + char *secretstr; + char *s, *n; + dns_fixedname_t fkeyname; + dns_name_t *mykeyname; + char *name; + const dns_name_t *hmacname = NULL; + uint16_t digestbits = 0; + + mykeyname = dns_fixedname_initname(&fkeyname); + + debug("Creating key..."); + + s = strchr(keystr, ':'); + if (s == NULL || s == keystr || s[1] == 0) { + fatal("key option must specify [hmac:]keyname:secret"); + } + secretstr = s + 1; + n = strchr(secretstr, ':'); + if (n != NULL) { + if (n == secretstr || n[1] == 0) { + fatal("key option must specify [hmac:]keyname:secret"); + } + name = secretstr; + secretstr = n + 1; + if (!parse_hmac(&hmacname, keystr, s - keystr, &digestbits)) { + exit(1); + } + } else { + hmacname = DNS_TSIG_HMACMD5_NAME; + name = keystr; + n = s; + } + + isc_buffer_init(&keynamesrc, name, (unsigned int)(n - name)); + isc_buffer_add(&keynamesrc, (unsigned int)(n - name)); + + debug("namefromtext"); + result = dns_name_fromtext(mykeyname, &keynamesrc, dns_rootname, 0, + NULL); + check_result(result, "dns_name_fromtext"); + + secretlen = strlen(secretstr) * 3 / 4; + secret = isc_mem_allocate(gmctx, secretlen); + + isc_buffer_init(&secretbuf, secret, secretlen); + result = isc_base64_decodestring(secretstr, &secretbuf); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", keystr, + isc_result_totext(result)); + goto failure; + } + + secretlen = isc_buffer_usedlength(&secretbuf); + + debug("keycreate"); + result = dns_tsigkey_create(mykeyname, hmacname, secret, secretlen, + false, NULL, 0, 0, gmctx, NULL, &tsigkey); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", keystr, + isc_result_totext(result)); + } else { + dst_key_setbits(tsigkey->key, digestbits); + } +failure: + if (secret != NULL) { + isc_mem_free(gmctx, secret); + } +} + +/* + * Get a key from a named.conf format keyfile + */ +static isc_result_t +read_sessionkey(isc_mem_t *mctx, isc_log_t *lctx) { + cfg_parser_t *pctx = NULL; + cfg_obj_t *sessionkey = NULL; + const cfg_obj_t *key = NULL; + const cfg_obj_t *secretobj = NULL; + const cfg_obj_t *algorithmobj = NULL; + const char *mykeyname; + const char *secretstr; + const char *algorithm; + isc_result_t result; + int len; + + if (!isc_file_exists(keyfile)) { + return (ISC_R_FILENOTFOUND); + } + + result = cfg_parser_create(mctx, lctx, &pctx); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey, + &sessionkey); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_map_get(sessionkey, "key", &key); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + (void)cfg_map_get(key, "secret", &secretobj); + (void)cfg_map_get(key, "algorithm", &algorithmobj); + if (secretobj == NULL || algorithmobj == NULL) { + fatal("key must have algorithm and secret"); + } + + mykeyname = cfg_obj_asstring(cfg_map_getname(key)); + secretstr = cfg_obj_asstring(secretobj); + algorithm = cfg_obj_asstring(algorithmobj); + + len = strlen(algorithm) + strlen(mykeyname) + strlen(secretstr) + 3; + keystr = isc_mem_allocate(mctx, len); + snprintf(keystr, len, "%s:%s:%s", algorithm, mykeyname, secretstr); + setup_keystr(); + +cleanup: + if (pctx != NULL) { + if (sessionkey != NULL) { + cfg_obj_destroy(pctx, &sessionkey); + } + cfg_parser_destroy(&pctx); + } + + if (keystr != NULL) { + isc_mem_free(mctx, keystr); + } + + return (result); +} + +static void +setup_keyfile(isc_mem_t *mctx, isc_log_t *lctx) { + dst_key_t *dstkey = NULL; + isc_result_t result; + const dns_name_t *hmacname = NULL; + + debug("Creating key..."); + + if (sig0key != NULL) { + dst_key_free(&sig0key); + } + + /* Try reading the key from a K* pair */ + result = dst_key_fromnamedfile( + keyfile, NULL, DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx, &dstkey); + + /* If that didn't work, try reading it as a session.key keyfile */ + if (result != ISC_R_SUCCESS) { + result = read_sessionkey(mctx, lctx); + if (result == ISC_R_SUCCESS) { + return; + } + } + + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "could not read key from %.*s.{private,key}: " + "%s\n", + basenamelen(keyfile), keyfile, + isc_result_totext(result)); + return; + } + + switch (dst_key_alg(dstkey)) { + case DST_ALG_HMACMD5: + hmacname = DNS_TSIG_HMACMD5_NAME; + break; + case DST_ALG_HMACSHA1: + hmacname = DNS_TSIG_HMACSHA1_NAME; + break; + case DST_ALG_HMACSHA224: + hmacname = DNS_TSIG_HMACSHA224_NAME; + break; + case DST_ALG_HMACSHA256: + hmacname = DNS_TSIG_HMACSHA256_NAME; + break; + case DST_ALG_HMACSHA384: + hmacname = DNS_TSIG_HMACSHA384_NAME; + break; + case DST_ALG_HMACSHA512: + hmacname = DNS_TSIG_HMACSHA512_NAME; + break; + } + if (hmacname != NULL) { + result = dns_tsigkey_createfromkey( + dst_key_name(dstkey), hmacname, dstkey, false, NULL, 0, + 0, mctx, NULL, &tsigkey); + dst_key_free(&dstkey); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", + keyfile, isc_result_totext(result)); + return; + } + } else { + dst_key_attach(dstkey, &sig0key); + dst_key_free(&dstkey); + } +} + +static void +doshutdown(void) { + isc_task_detach(&global_task); + + /* + * The isc_mem_put of primary_servers must be before the + * isc_mem_put of servers as it sets the servers pointer + * to NULL. + */ + if (primary_servers != NULL && primary_servers != servers) { + isc_mem_put(gmctx, primary_servers, + primary_alloc * sizeof(isc_sockaddr_t)); + } + + if (servers != NULL) { + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + } + + if (localaddr4 != NULL) { + isc_mem_put(gmctx, localaddr4, sizeof(isc_sockaddr_t)); + } + + if (localaddr6 != NULL) { + isc_mem_put(gmctx, localaddr6, sizeof(isc_sockaddr_t)); + } + + if (tsigkey != NULL) { + ddebug("Freeing TSIG key"); + dns_tsigkey_detach(&tsigkey); + } + + if (sig0key != NULL) { + ddebug("Freeing SIG(0) key"); + dst_key_free(&sig0key); + } + + if (updatemsg != NULL) { + dns_message_detach(&updatemsg); + } + + ddebug("Destroying request manager"); + dns_requestmgr_detach(&requestmgr); + + ddebug("Freeing the dispatchers"); + if (have_ipv4) { + dns_dispatch_detach(&dispatchv4); + } + if (have_ipv6) { + dns_dispatch_detach(&dispatchv6); + } + + ddebug("Shutting down dispatch manager"); + dns_dispatchmgr_detach(&dispatchmgr); +} + +static void +maybeshutdown(void) { + /* when called from getinput, doshutdown might be already finished */ + if (requestmgr == NULL) { + return; + } + + ddebug("Shutting down request manager"); + dns_requestmgr_shutdown(requestmgr); + + if (requests != 0) { + return; + } + + doshutdown(); +} + +static void +shutdown_program(isc_task_t *task, isc_event_t *event) { + REQUIRE(task == global_task); + UNUSED(task); + + ddebug("shutdown_program()"); + isc_event_free(&event); + + shuttingdown = true; + maybeshutdown(); +} + +/* + * Try honoring the operating system's preferred ephemeral port range. + */ +static void +set_source_ports(dns_dispatchmgr_t *manager) { + isc_portset_t *v4portset = NULL, *v6portset = NULL; + in_port_t udpport_low, udpport_high; + isc_result_t result; + + result = isc_portset_create(gmctx, &v4portset); + check_result(result, "isc_portset_create (v4)"); + result = isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high); + check_result(result, "isc_net_getudpportrange (v4)"); + isc_portset_addrange(v4portset, udpport_low, udpport_high); + + result = isc_portset_create(gmctx, &v6portset); + check_result(result, "isc_portset_create (v6)"); + result = isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high); + check_result(result, "isc_net_getudpportrange (v6)"); + isc_portset_addrange(v6portset, udpport_low, udpport_high); + + result = dns_dispatchmgr_setavailports(manager, v4portset, v6portset); + check_result(result, "dns_dispatchmgr_setavailports"); + + isc_portset_destroy(gmctx, &v4portset); + isc_portset_destroy(gmctx, &v6portset); +} + +static void +setup_system(void) { + isc_result_t result; + isc_sockaddr_t bind_any, bind_any6; + isc_sockaddrlist_t *nslist; + isc_logconfig_t *logconfig = NULL; + irs_resconf_t *resconf = NULL; + + ddebug("setup_system()"); + + isc_log_create(gmctx, &glctx, &logconfig); + isc_log_setcontext(glctx); + dns_log_init(glctx); + dns_log_setcontext(glctx); + + result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL); + check_result(result, "isc_log_usechannel"); + + isc_log_setdebuglevel(glctx, logdebuglevel); + + result = irs_resconf_load(gmctx, resolvconf, &resconf); + if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { + fatal("parse of %s failed", resolvconf); + } + + nslist = irs_resconf_getnameservers(resconf); + + if (servers != NULL) { + if (primary_servers == servers) { + primary_servers = NULL; + } + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + } + + ns_inuse = 0; + if (local_only || ISC_LIST_EMPTY(*nslist)) { + struct in_addr in; + struct in6_addr in6; + + if (local_only && keyfile == NULL) { + keyfile = SESSION_KEYFILE; + } + + default_servers = !local_only; + + ns_total = ns_alloc = (have_ipv4 ? 1 : 0) + (have_ipv6 ? 1 : 0); + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + + if (have_ipv6) { + memset(&in6, 0, sizeof(in6)); + in6.s6_addr[15] = 1; + isc_sockaddr_fromin6(&servers[0], &in6, dnsport); + } + if (have_ipv4) { + in.s_addr = htonl(INADDR_LOOPBACK); + isc_sockaddr_fromin(&servers[(have_ipv6 ? 1 : 0)], &in, + dnsport); + } + } else { + isc_sockaddr_t *sa; + int i; + + /* + * Count the nameservers (skipping any that we can't use + * because of address family restrictions) and allocate + * the servers array. + */ + ns_total = 0; + for (sa = ISC_LIST_HEAD(*nslist); sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) + { + switch (sa->type.sa.sa_family) { + case AF_INET: + if (have_ipv4) { + ns_total++; + } + break; + case AF_INET6: + if (have_ipv6) { + ns_total++; + } + break; + default: + fatal("bad family"); + } + } + + ns_alloc = ns_total; + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + + i = 0; + for (sa = ISC_LIST_HEAD(*nslist); sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) + { + switch (sa->type.sa.sa_family) { + case AF_INET: + if (have_ipv4) { + sa->type.sin.sin_port = htons(dnsport); + } else { + continue; + } + break; + case AF_INET6: + if (have_ipv6) { + sa->type.sin6.sin6_port = + htons(dnsport); + } else { + continue; + } + break; + default: + fatal("bad family"); + } + INSIST(i < ns_alloc); + servers[i++] = *sa; + } + } + + irs_resconf_destroy(&resconf); + + result = isc_managers_create(gmctx, 1, 0, &netmgr, &taskmgr, NULL); + check_result(result, "isc_managers_create"); + + result = dns_dispatchmgr_create(gmctx, netmgr, &dispatchmgr); + check_result(result, "dns_dispatchmgr_create"); + + result = isc_task_create(taskmgr, 0, &global_task); + check_result(result, "isc_task_create"); + + result = isc_task_onshutdown(global_task, shutdown_program, NULL); + check_result(result, "isc_task_onshutdown"); + + result = dst_lib_init(gmctx, NULL); + check_result(result, "dst_lib_init"); + is_dst_up = true; + + set_source_ports(dispatchmgr); + + if (have_ipv6) { + isc_sockaddr_any6(&bind_any6); + result = dns_dispatch_createudp(dispatchmgr, &bind_any6, + &dispatchv6); + check_result(result, "dns_dispatch_createudp (v6)"); + } + + if (have_ipv4) { + isc_sockaddr_any(&bind_any); + result = dns_dispatch_createudp(dispatchmgr, &bind_any, + &dispatchv4); + check_result(result, "dns_dispatch_createudp (v4)"); + } + + result = dns_requestmgr_create(gmctx, taskmgr, dispatchmgr, dispatchv4, + dispatchv6, &requestmgr); + check_result(result, "dns_requestmgr_create"); + + if (keystr != NULL) { + setup_keystr(); + } else if (local_only) { + result = read_sessionkey(gmctx, glctx); + if (result != ISC_R_SUCCESS) { + fatal("can't read key from %s: %s\n", keyfile, + isc_result_totext(result)); + } + } else if (keyfile != NULL) { + setup_keyfile(gmctx, glctx); + } + + isc_mutex_init(&answer_lock); +} + +static int +get_addresses(char *host, in_port_t port, isc_sockaddr_t *sockaddr, + int naddrs) { + int count = 0; + isc_result_t result; + + isc_app_block(); + result = bind9_getaddresses(host, port, sockaddr, naddrs, &count); + isc_app_unblock(); + if (result != ISC_R_SUCCESS) { + error("couldn't get address for '%s': %s", host, + isc_result_totext(result)); + } + return (count); +} + +static void +version(void) { + fprintf(stderr, "nsupdate %s\n", PACKAGE_VERSION); +} + +#define PARSE_ARGS_FMT "46C:dDghilL:Mok:p:Pr:R:t:Tu:vVy:" + +static void +pre_parse_args(int argc, char **argv) { + dns_rdatatype_t t; + int ch; + char buf[100]; + bool doexit = false; + bool ipv4only = false, ipv6only = false; + + while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) { + switch (ch) { + case 'M': /* was -dm */ + debugging = true; + ddebugging = true; + memdebugging = true; + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + + case '?': + case 'h': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + argv[0], isc_commandline_option); + } + fprintf(stderr, "usage: nsupdate [-CdDi] [-L level] " + "[-l] [-g | -o | -y keyname:secret " + "| -k keyfile] [-p port] " + "[-v] [-V] [-P] [-T] [-4 | -6] " + "[filename]\n"); + exit(1); + + case 'P': + for (t = 0xff00; t <= 0xfffe; t++) { + if (dns_rdatatype_ismeta(t)) { + continue; + } + dns_rdatatype_format(t, buf, sizeof(buf)); + if (strncmp(buf, "TYPE", 4) != 0) { + fprintf(stdout, "%s\n", buf); + } + } + doexit = true; + break; + + case 'T': + for (t = 1; t <= 0xfeff; t++) { + if (dns_rdatatype_ismeta(t)) { + continue; + } + dns_rdatatype_format(t, buf, sizeof(buf)); + if (strncmp(buf, "TYPE", 4) != 0) { + fprintf(stdout, "%s\n", buf); + } + } + doexit = true; + break; + + case 'V': + version(); + doexit = true; + break; + + default: + break; + } + } + if (doexit) { + exit(0); + } + isc_commandline_reset = true; + isc_commandline_index = 1; +} + +static void +parse_args(int argc, char **argv) { + int ch; + uint32_t i; + isc_result_t result; + bool force_interactive = false; + + debug("parse_args"); + while ((ch = isc_commandline_parse(argc, argv, PARSE_ARGS_FMT)) != -1) { + switch (ch) { + case '4': + if (have_ipv4) { + isc_net_disableipv6(); + have_ipv6 = false; + } else { + fatal("can't find IPv4 networking"); + } + break; + case '6': + if (have_ipv6) { + isc_net_disableipv4(); + have_ipv4 = false; + } else { + fatal("can't find IPv6 networking"); + } + break; + case 'C': + resolvconf = isc_commandline_argument; + break; + case 'd': + debugging = true; + break; + case 'D': /* was -dd */ + debugging = true; + ddebugging = true; + break; + case 'M': + break; + case 'i': + force_interactive = true; + interactive = true; + break; + case 'l': + local_only = true; + break; + case 'L': + result = isc_parse_uint32(&i, isc_commandline_argument, + 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "bad library debug value " + "'%s'\n", + isc_commandline_argument); + exit(1); + } + logdebuglevel = i; + break; + case 'y': + keystr = isc_commandline_argument; + break; + case 'v': + usevc = true; + break; + case 'k': + keyfile = isc_commandline_argument; + break; + case 'g': + usegsstsig = true; + use_win2k_gsstsig = false; + break; + case 'o': + usegsstsig = true; + use_win2k_gsstsig = true; + break; + case 'p': + result = isc_parse_uint16(&dnsport, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "bad port number " + "'%s'\n", + isc_commandline_argument); + exit(1); + } + break; + case 't': + result = isc_parse_uint32(&timeout, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad timeout '%s'\n", + isc_commandline_argument); + exit(1); + } + if (timeout == 0) { + timeout = UINT_MAX; + } + break; + case 'u': + result = isc_parse_uint32(&udp_timeout, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad udp timeout '%s'\n", + isc_commandline_argument); + exit(1); + } + break; + case 'r': + result = isc_parse_uint32(&udp_retries, + isc_commandline_argument, 10); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "bad udp retries '%s'\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'R': + fatal("The -R option has been deprecated."); + break; + + default: + fprintf(stderr, "%s: unhandled option: %c\n", argv[0], + isc_commandline_option); + exit(1); + } + } + if (keyfile != NULL && keystr != NULL) { + fprintf(stderr, "%s: cannot specify both -k and -y\n", argv[0]); + exit(1); + } + +#if HAVE_GSSAPI + if (usegsstsig && (keyfile != NULL || keystr != NULL)) { + fprintf(stderr, "%s: cannot specify -g with -k or -y\n", + argv[0]); + exit(1); + } +#else /* HAVE_GSSAPI */ + if (usegsstsig) { + fprintf(stderr, + "%s: cannot specify -g or -o, " + "program not linked with GSS API Library\n", + argv[0]); + exit(1); + } +#endif /* HAVE_GSSAPI */ + + if (argv[isc_commandline_index] != NULL) { + if (strcmp(argv[isc_commandline_index], "-") == 0) { + input = stdin; + } else { + result = isc_stdio_open(argv[isc_commandline_index], + "r", &input); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not open '%s': %s\n", + argv[isc_commandline_index], + isc_result_totext(result)); + exit(1); + } + } + if (!force_interactive) { + interactive = false; + } + } +} + +static uint16_t +parse_name(char **cmdlinep, dns_message_t *msg, dns_name_t **namep) { + isc_result_t result; + char *word; + isc_buffer_t source; + + word = nsu_strsep(cmdlinep, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read owner name\n"); + return (STATUS_SYNTAX); + } + + result = dns_message_gettempname(msg, namep); + check_result(result, "dns_message_gettempname"); + isc_buffer_init(&source, word, strlen(word)); + isc_buffer_add(&source, strlen(word)); + result = dns_name_fromtext(*namep, &source, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + error("invalid owner name: %s", isc_result_totext(result)); + isc_buffer_invalidate(&source); + dns_message_puttempname(msg, namep); + return (STATUS_SYNTAX); + } + isc_buffer_invalidate(&source); + return (STATUS_MORE); +} + +static uint16_t +parse_rdata(char **cmdlinep, dns_rdataclass_t rdataclass, + dns_rdatatype_t rdatatype, dns_message_t *msg, dns_rdata_t *rdata) { + char *cmdline = *cmdlinep; + isc_buffer_t source, *buf = NULL, *newbuf = NULL; + isc_region_t r; + isc_lex_t *lex = NULL; + dns_rdatacallbacks_t callbacks; + isc_result_t result; + + if (cmdline == NULL) { + rdata->flags = DNS_RDATA_UPDATE; + return (STATUS_MORE); + } + + while (*cmdline != 0 && isspace((unsigned char)*cmdline)) { + cmdline++; + } + + if (*cmdline != 0) { + dns_rdatacallbacks_init(&callbacks); + result = isc_lex_create(gmctx, strlen(cmdline), &lex); + check_result(result, "isc_lex_create"); + isc_buffer_init(&source, cmdline, strlen(cmdline)); + isc_buffer_add(&source, strlen(cmdline)); + result = isc_lex_openbuffer(lex, &source); + check_result(result, "isc_lex_openbuffer"); + isc_buffer_allocate(gmctx, &buf, MAXWIRE); + result = dns_rdata_fromtext(NULL, rdataclass, rdatatype, lex, + dns_rootname, 0, gmctx, buf, + &callbacks); + isc_lex_destroy(&lex); + if (result == ISC_R_SUCCESS) { + isc_buffer_usedregion(buf, &r); + isc_buffer_allocate(gmctx, &newbuf, r.length); + isc_buffer_putmem(newbuf, r.base, r.length); + isc_buffer_usedregion(newbuf, &r); + dns_rdata_fromregion(rdata, rdataclass, rdatatype, &r); + isc_buffer_free(&buf); + dns_message_takebuffer(msg, &newbuf); + } else { + fprintf(stderr, "invalid rdata format: %s\n", + isc_result_totext(result)); + isc_buffer_free(&buf); + return (STATUS_SYNTAX); + } + } else { + rdata->flags = DNS_RDATA_UPDATE; + } + *cmdlinep = cmdline; + return (STATUS_MORE); +} + +static uint16_t +make_prereq(char *cmdline, bool ispositive, bool isrrset) { + isc_result_t result; + char *word; + dns_name_t *name = NULL; + isc_textregion_t region; + dns_rdataset_t *rdataset = NULL; + dns_rdatalist_t *rdatalist = NULL; + dns_rdataclass_t rdataclass; + dns_rdatatype_t rdatatype; + dns_rdata_t *rdata = NULL; + uint16_t retval; + + ddebug("make_prereq()"); + + /* + * Read the owner name + */ + retval = parse_name(&cmdline, updatemsg, &name); + if (retval != STATUS_MORE) { + return (retval); + } + + /* + * If this is an rrset prereq, read the class or type. + */ + if (isrrset) { + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read class or type\n"); + goto failure; + } + region.base = word; + region.length = strlen(word); + result = dns_rdataclass_fromtext(&rdataclass, ®ion); + if (result == ISC_R_SUCCESS) { + if (!setzoneclass(rdataclass)) { + fprintf(stderr, "class mismatch: %s\n", word); + goto failure; + } + /* + * Now read the type. + */ + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read type\n"); + goto failure; + } + region.base = word; + region.length = strlen(word); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "invalid type: %s\n", word); + goto failure; + } + } else { + rdataclass = getzoneclass(); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "invalid type: %s\n", word); + goto failure; + } + } + } else { + rdatatype = dns_rdatatype_any; + } + + result = dns_message_gettemprdata(updatemsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + dns_rdata_init(rdata); + + if (isrrset && ispositive) { + retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg, + rdata); + if (retval != STATUS_MORE) { + goto failure; + } + } else { + rdata->flags = DNS_RDATA_UPDATE; + } + + result = dns_message_gettemprdatalist(updatemsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + rdatalist->type = rdatatype; + if (ispositive) { + if (isrrset && rdata->data != NULL) { + rdatalist->rdclass = rdataclass; + } else { + rdatalist->rdclass = dns_rdataclass_any; + } + } else { + rdatalist->rdclass = dns_rdataclass_none; + } + rdata->rdclass = rdatalist->rdclass; + rdata->type = rdatatype; + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + dns_rdatalist_tordataset(rdatalist, rdataset); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_PREREQUISITE); + return (STATUS_MORE); + +failure: + if (name != NULL) { + dns_message_puttempname(updatemsg, &name); + } + return (STATUS_SYNTAX); +} + +static uint16_t +evaluate_prereq(char *cmdline) { + char *word; + bool ispositive, isrrset; + + ddebug("evaluate_prereq()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read operation code\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "nxdomain") == 0) { + ispositive = false; + isrrset = false; + } else if (strcasecmp(word, "yxdomain") == 0) { + ispositive = true; + isrrset = false; + } else if (strcasecmp(word, "nxrrset") == 0) { + ispositive = false; + isrrset = true; + } else if (strcasecmp(word, "yxrrset") == 0) { + ispositive = true; + isrrset = true; + } else { + fprintf(stderr, "incorrect operation code: %s\n", word); + return (STATUS_SYNTAX); + } + return (make_prereq(cmdline, ispositive, isrrset)); +} + +static uint16_t +evaluate_server(char *cmdline) { + char *word, *server; + long port; + + if (local_only) { + fprintf(stderr, "cannot reset server in localhost-only mode\n"); + return (STATUS_SYNTAX); + } + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read server name\n"); + return (STATUS_SYNTAX); + } + server = word; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + port = dnsport; + } else { + char *endp; + port = strtol(word, &endp, 10); + if (*endp != 0) { + fprintf(stderr, "port '%s' is not numeric\n", word); + return (STATUS_SYNTAX); + } else if (port < 1 || port > 65535) { + fprintf(stderr, + "port '%s' is out of range " + "(1 to 65535)\n", + word); + return (STATUS_SYNTAX); + } + } + + if (servers != NULL) { + if (primary_servers == servers) { + primary_servers = NULL; + } + isc_mem_put(gmctx, servers, ns_alloc * sizeof(isc_sockaddr_t)); + } + + default_servers = false; + + ns_alloc = MAX_SERVERADDRS; + ns_inuse = 0; + servers = isc_mem_get(gmctx, ns_alloc * sizeof(isc_sockaddr_t)); + + memset(servers, 0, ns_alloc * sizeof(isc_sockaddr_t)); + ns_total = get_addresses(server, (in_port_t)port, servers, ns_alloc); + if (ns_total == 0) { + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_local(char *cmdline) { + char *word, *local; + long port; + struct in_addr in4; + struct in6_addr in6; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read server name\n"); + return (STATUS_SYNTAX); + } + local = word; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + port = 0; + } else { + char *endp; + port = strtol(word, &endp, 10); + if (*endp != 0) { + fprintf(stderr, "port '%s' is not numeric\n", word); + return (STATUS_SYNTAX); + } else if (port < 1 || port > 65535) { + fprintf(stderr, + "port '%s' is out of range " + "(1 to 65535)\n", + word); + return (STATUS_SYNTAX); + } + } + + if (have_ipv6 && inet_pton(AF_INET6, local, &in6) == 1) { + if (localaddr6 == NULL) { + localaddr6 = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + } + isc_sockaddr_fromin6(localaddr6, &in6, (in_port_t)port); + } else if (have_ipv4 && inet_pton(AF_INET, local, &in4) == 1) { + if (localaddr4 == NULL) { + localaddr4 = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + } + isc_sockaddr_fromin(localaddr4, &in4, (in_port_t)port); + } else { + fprintf(stderr, "invalid address %s", local); + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_key(char *cmdline) { + char *namestr; + char *secretstr; + isc_buffer_t b; + isc_result_t result; + dns_fixedname_t fkeyname; + dns_name_t *mykeyname; + int secretlen; + unsigned char *secret = NULL; + isc_buffer_t secretbuf; + const dns_name_t *hmacname = NULL; + uint16_t digestbits = 0; + char *n; + + namestr = nsu_strsep(&cmdline, " \t\r\n"); + if (namestr == NULL || *namestr == 0) { + fprintf(stderr, "could not read key name\n"); + return (STATUS_SYNTAX); + } + + mykeyname = dns_fixedname_initname(&fkeyname); + + n = strchr(namestr, ':'); + if (n != NULL) { + if (!parse_hmac(&hmacname, namestr, n - namestr, &digestbits)) { + return (STATUS_SYNTAX); + } + namestr = n + 1; + } else { + hmacname = DNS_TSIG_HMACMD5_NAME; + } + + isc_buffer_init(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + result = dns_name_fromtext(mykeyname, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not parse key name\n"); + return (STATUS_SYNTAX); + } + + secretstr = nsu_strsep(&cmdline, "\r\n"); + if (secretstr == NULL || *secretstr == 0) { + fprintf(stderr, "could not read key secret\n"); + return (STATUS_SYNTAX); + } + secretlen = strlen(secretstr) * 3 / 4; + secret = isc_mem_allocate(gmctx, secretlen); + + isc_buffer_init(&secretbuf, secret, secretlen); + result = isc_base64_decodestring(secretstr, &secretbuf); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s: %s\n", secretstr, + isc_result_totext(result)); + isc_mem_free(gmctx, secret); + return (STATUS_SYNTAX); + } + secretlen = isc_buffer_usedlength(&secretbuf); + + if (tsigkey != NULL) { + dns_tsigkey_detach(&tsigkey); + } + result = dns_tsigkey_create(mykeyname, hmacname, secret, secretlen, + false, NULL, 0, 0, gmctx, NULL, &tsigkey); + isc_mem_free(gmctx, secret); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not create key from %s %s: %s\n", + namestr, secretstr, isc_result_totext(result)); + return (STATUS_SYNTAX); + } + dst_key_setbits(tsigkey->key, digestbits); + return (STATUS_MORE); +} + +static uint16_t +evaluate_zone(char *cmdline) { + char *word; + isc_buffer_t b; + isc_result_t result; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read zone name\n"); + return (STATUS_SYNTAX); + } + + userzone = dns_fixedname_initname(&fuserzone); + isc_buffer_init(&b, word, strlen(word)); + isc_buffer_add(&b, strlen(word)); + result = dns_name_fromtext(userzone, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + userzone = NULL; /* Lest it point to an invalid name */ + fprintf(stderr, "could not parse zone name\n"); + return (STATUS_SYNTAX); + } + + return (STATUS_MORE); +} + +static uint16_t +evaluate_realm(char *cmdline) { +#if HAVE_GSSAPI + char *word; + char buf[1024]; + int n; + + if (realm != NULL) { + isc_mem_free(gmctx, realm); + realm = NULL; + } + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + return (STATUS_MORE); + } + + n = snprintf(buf, sizeof(buf), "@%s", word); + if (n < 0 || (size_t)n >= sizeof(buf)) { + error("realm is too long"); + return (STATUS_SYNTAX); + } + realm = isc_mem_strdup(gmctx, buf); + return (STATUS_MORE); +#else /* HAVE_GSSAPI */ + UNUSED(cmdline); + return (STATUS_SYNTAX); +#endif /* HAVE_GSSAPI */ +} + +static uint16_t +evaluate_ttl(char *cmdline) { + char *word; + isc_result_t result; + uint32_t ttl; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read ttl\n"); + return (STATUS_SYNTAX); + } + + if (!strcasecmp(word, "none")) { + default_ttl = 0; + default_ttl_set = false; + return (STATUS_MORE); + } + + result = isc_parse_uint32(&ttl, word, 10); + if (result != ISC_R_SUCCESS) { + return (STATUS_SYNTAX); + } + + if (ttl > TTL_MAX) { + fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n", word, + TTL_MAX); + return (STATUS_SYNTAX); + } + default_ttl = ttl; + default_ttl_set = true; + + return (STATUS_MORE); +} + +static uint16_t +evaluate_class(char *cmdline) { + char *word; + isc_textregion_t r; + isc_result_t result; + dns_rdataclass_t rdclass; + + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read class name\n"); + return (STATUS_SYNTAX); + } + + r.base = word; + r.length = strlen(word); + result = dns_rdataclass_fromtext(&rdclass, &r); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not parse class name: %s\n", word); + return (STATUS_SYNTAX); + } + switch (rdclass) { + case dns_rdataclass_none: + case dns_rdataclass_any: + case dns_rdataclass_reserved0: + fprintf(stderr, "bad default class: %s\n", word); + return (STATUS_SYNTAX); + default: + defaultclass = rdclass; + } + + return (STATUS_MORE); +} + +static uint16_t +update_addordelete(char *cmdline, bool isdelete) { + isc_result_t result; + dns_name_t *name = NULL; + uint32_t ttl; + char *word; + dns_rdataclass_t rdataclass; + dns_rdatatype_t rdatatype; + dns_rdata_t *rdata = NULL; + dns_rdatalist_t *rdatalist = NULL; + dns_rdataset_t *rdataset = NULL; + isc_textregion_t region; + uint16_t retval; + + ddebug("update_addordelete()"); + + /* + * Read the owner name. + */ + retval = parse_name(&cmdline, updatemsg, &name); + if (retval != STATUS_MORE) { + return (retval); + } + + result = dns_message_gettemprdata(updatemsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + dns_rdata_init(rdata); + + /* + * If this is an add, read the TTL and verify that it's in range. + * If it's a delete, ignore a TTL if present (for compatibility). + */ + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + if (!isdelete) { + fprintf(stderr, "could not read owner ttl\n"); + goto failure; + } else { + ttl = 0; + rdataclass = dns_rdataclass_any; + rdatatype = dns_rdatatype_any; + rdata->flags = DNS_RDATA_UPDATE; + goto doneparsing; + } + } + result = isc_parse_uint32(&ttl, word, 10); + if (result != ISC_R_SUCCESS) { + if (isdelete) { + ttl = 0; + goto parseclass; + } else if (default_ttl_set) { + ttl = default_ttl; + goto parseclass; + } else { + fprintf(stderr, "ttl '%s': %s\n", word, + isc_result_totext(result)); + goto failure; + } + } + + if (isdelete) { + ttl = 0; + } else if (ttl > TTL_MAX) { + fprintf(stderr, "ttl '%s' is out of range (0 to %u)\n", word, + TTL_MAX); + goto failure; + } + + /* + * Read the class or type. + */ + word = nsu_strsep(&cmdline, " \t\r\n"); +parseclass: + if (word == NULL || *word == 0) { + if (isdelete) { + rdataclass = dns_rdataclass_any; + rdatatype = dns_rdatatype_any; + rdata->flags = DNS_RDATA_UPDATE; + goto doneparsing; + } else { + fprintf(stderr, "could not read class or type\n"); + goto failure; + } + } + region.base = word; + region.length = strlen(word); + rdataclass = dns_rdataclass_any; + result = dns_rdataclass_fromtext(&rdataclass, ®ion); + if (result == ISC_R_SUCCESS && rdataclass != dns_rdataclass_any) { + if (!setzoneclass(rdataclass)) { + fprintf(stderr, "class mismatch: %s\n", word); + goto failure; + } + /* + * Now read the type. + */ + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + if (isdelete) { + rdataclass = dns_rdataclass_any; + rdatatype = dns_rdatatype_any; + rdata->flags = DNS_RDATA_UPDATE; + goto doneparsing; + } else { + fprintf(stderr, "could not read type\n"); + goto failure; + } + } + region.base = word; + region.length = strlen(word); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "'%s' is not a valid type: %s\n", word, + isc_result_totext(result)); + goto failure; + } + } else { + rdataclass = getzoneclass(); + result = dns_rdatatype_fromtext(&rdatatype, ®ion); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, + "'%s' is not a valid class or type: " + "%s\n", + word, isc_result_totext(result)); + goto failure; + } + } + + retval = parse_rdata(&cmdline, rdataclass, rdatatype, updatemsg, rdata); + if (retval != STATUS_MORE) { + goto failure; + } + + if (isdelete) { + if ((rdata->flags & DNS_RDATA_UPDATE) != 0) { + rdataclass = dns_rdataclass_any; + } else { + rdataclass = dns_rdataclass_none; + } + } else { + if ((rdata->flags & DNS_RDATA_UPDATE) != 0) { + fprintf(stderr, "could not read rdata\n"); + goto failure; + } + } + + if (!isdelete && checknames) { + dns_fixedname_t fixed; + dns_name_t *bad; + + if (!dns_rdata_checkowner(name, rdata->rdclass, rdata->type, + true)) + { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(name, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad owner '%s'\n", + namebuf); + goto failure; + } + + bad = dns_fixedname_initname(&fixed); + if (!dns_rdata_checknames(rdata, name, bad)) { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(bad, namebuf, sizeof(namebuf)); + fprintf(stderr, "check-names failed: bad name '%s'\n", + namebuf); + goto failure; + } + } + + if (!isdelete && rdata->type == dns_rdatatype_nsec3param) { + dns_rdata_nsec3param_t nsec3param; + + result = dns_rdata_tostruct(rdata, &nsec3param, NULL); + check_result(result, "dns_rdata_tostruct"); + if (nsec3param.iterations > dns_nsec3_maxiterations()) { + fprintf(stderr, + "NSEC3PARAM has excessive iterations (> %u)\n", + dns_nsec3_maxiterations()); + goto failure; + } + } + +doneparsing: + + result = dns_message_gettemprdatalist(updatemsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + rdatalist->type = rdatatype; + rdatalist->rdclass = rdataclass; + rdatalist->covers = rdatatype; + rdatalist->ttl = (dns_ttl_t)ttl; + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + dns_rdatalist_tordataset(rdatalist, rdataset); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_UPDATE); + return (STATUS_MORE); + +failure: + if (name != NULL) { + dns_message_puttempname(updatemsg, &name); + } + dns_message_puttemprdata(updatemsg, &rdata); + return (STATUS_SYNTAX); +} + +static uint16_t +evaluate_update(char *cmdline) { + char *word; + bool isdelete; + + ddebug("evaluate_update()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read operation code\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "delete") == 0) { + isdelete = true; + } else if (strcasecmp(word, "del") == 0) { + isdelete = true; + } else if (strcasecmp(word, "add") == 0) { + isdelete = false; + } else { + fprintf(stderr, "incorrect operation code: %s\n", word); + return (STATUS_SYNTAX); + } + return (update_addordelete(cmdline, isdelete)); +} + +static uint16_t +evaluate_checknames(char *cmdline) { + char *word; + + ddebug("evaluate_checknames()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + if (word == NULL || *word == 0) { + fprintf(stderr, "could not read check-names directive\n"); + return (STATUS_SYNTAX); + } + if (strcasecmp(word, "yes") == 0 || strcasecmp(word, "true") == 0 || + strcasecmp(word, "on") == 0) + { + checknames = true; + } else if (strcasecmp(word, "no") == 0 || + strcasecmp(word, "false") == 0 || + strcasecmp(word, "off") == 0) + { + checknames = false; + } else { + fprintf(stderr, "incorrect check-names directive: %s\n", word); + return (STATUS_SYNTAX); + } + return (STATUS_MORE); +} + +static void +setzone(dns_name_t *zonename) { + isc_result_t result; + dns_name_t *name = NULL; + dns_rdataset_t *rdataset = NULL; + + result = dns_message_firstname(updatemsg, DNS_SECTION_ZONE); + if (result == ISC_R_SUCCESS) { + dns_message_currentname(updatemsg, DNS_SECTION_ZONE, &name); + dns_message_removename(updatemsg, name, DNS_SECTION_ZONE); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_HEAD(name->list)) + { + ISC_LIST_UNLINK(name->list, rdataset, link); + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(updatemsg, &rdataset); + } + dns_message_puttempname(updatemsg, &name); + } + + if (zonename != NULL) { + result = dns_message_gettempname(updatemsg, &name); + check_result(result, "dns_message_gettempname"); + dns_name_clone(zonename, name); + result = dns_message_gettemprdataset(updatemsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + dns_rdataset_makequestion(rdataset, getzoneclass(), + dns_rdatatype_soa); + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(updatemsg, name, DNS_SECTION_ZONE); + } +} + +static void +show_message(FILE *stream, dns_message_t *msg, const char *description) { + isc_result_t result; + isc_buffer_t *buf = NULL; + int bufsz; + + ddebug("show_message()"); + + setzone(userzone); + + bufsz = INITTEXT; + do { + if (bufsz > MAXTEXT) { + fprintf(stderr, "could not allocate large enough " + "buffer to display message\n"); + exit(1); + } + if (buf != NULL) { + isc_buffer_free(&buf); + } + isc_buffer_allocate(gmctx, &buf, bufsz); + result = dns_message_totext(msg, style, 0, buf); + bufsz *= 2; + } while (result == ISC_R_NOSPACE); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "could not convert message to text format.\n"); + isc_buffer_free(&buf); + return; + } + fprintf(stream, "%s\n%.*s", description, + (int)isc_buffer_usedlength(buf), (char *)isc_buffer_base(buf)); + fflush(stream); + isc_buffer_free(&buf); +} + +static uint16_t +do_next_command(char *cmdline) { + char *word; + + ddebug("do_next_command()"); + word = nsu_strsep(&cmdline, " \t\r\n"); + + if (word == NULL || *word == 0) { + return (STATUS_SEND); + } + if (word[0] == ';') { + return (STATUS_MORE); + } + if (strcasecmp(word, "quit") == 0) { + return (STATUS_QUIT); + } + if (strcasecmp(word, "prereq") == 0) { + return (evaluate_prereq(cmdline)); + } + if (strcasecmp(word, "nxdomain") == 0) { + return (make_prereq(cmdline, false, false)); + } + if (strcasecmp(word, "yxdomain") == 0) { + return (make_prereq(cmdline, true, false)); + } + if (strcasecmp(word, "nxrrset") == 0) { + return (make_prereq(cmdline, false, true)); + } + if (strcasecmp(word, "yxrrset") == 0) { + return (make_prereq(cmdline, true, true)); + } + if (strcasecmp(word, "update") == 0) { + return (evaluate_update(cmdline)); + } + if (strcasecmp(word, "delete") == 0) { + return (update_addordelete(cmdline, true)); + } + if (strcasecmp(word, "del") == 0) { + return (update_addordelete(cmdline, true)); + } + if (strcasecmp(word, "add") == 0) { + return (update_addordelete(cmdline, false)); + } + if (strcasecmp(word, "server") == 0) { + return (evaluate_server(cmdline)); + } + if (strcasecmp(word, "local") == 0) { + return (evaluate_local(cmdline)); + } + if (strcasecmp(word, "zone") == 0) { + return (evaluate_zone(cmdline)); + } + if (strcasecmp(word, "class") == 0) { + return (evaluate_class(cmdline)); + } + if (strcasecmp(word, "send") == 0) { + return (STATUS_SEND); + } + if (strcasecmp(word, "debug") == 0) { + if (debugging) { + ddebugging = true; + } else { + debugging = true; + } + return (STATUS_MORE); + } + if (strcasecmp(word, "ttl") == 0) { + return (evaluate_ttl(cmdline)); + } + if (strcasecmp(word, "show") == 0) { + show_message(stdout, updatemsg, "Outgoing update query:"); + return (STATUS_MORE); + } + if (strcasecmp(word, "answer") == 0) { + LOCK(&answer_lock); + if (answer != NULL) { + show_message(stdout, answer, "Answer:"); + } + UNLOCK(&answer_lock); + return (STATUS_MORE); + } + if (strcasecmp(word, "key") == 0) { + usegsstsig = false; + return (evaluate_key(cmdline)); + } + if (strcasecmp(word, "realm") == 0) { + return (evaluate_realm(cmdline)); + } + if (strcasecmp(word, "check-names") == 0 || + strcasecmp(word, "checknames") == 0) + { + return (evaluate_checknames(cmdline)); + } + if (strcasecmp(word, "gsstsig") == 0) { +#if HAVE_GSSAPI + usegsstsig = true; + use_win2k_gsstsig = false; +#else /* HAVE_GSSAPI */ + fprintf(stderr, "gsstsig not supported\n"); +#endif /* HAVE_GSSAPI */ + return (STATUS_MORE); + } + if (strcasecmp(word, "oldgsstsig") == 0) { +#if HAVE_GSSAPI + usegsstsig = true; + use_win2k_gsstsig = true; +#else /* HAVE_GSSAPI */ + fprintf(stderr, "gsstsig not supported\n"); +#endif /* HAVE_GSSAPI */ + return (STATUS_MORE); + } + if (strcasecmp(word, "help") == 0) { + fprintf(stdout, "nsupdate " PACKAGE_VERSION ":\n" + "local address [port] (set local " + "resolver)\n" + "server address [port] (set primary server " + "for zone)\n" + "send (send the update " + "request)\n" + "show (show the update " + "request)\n" + "answer (show the answer to " + "the last request)\n" + "quit (quit, any pending " + "update is not sent)\n" + "help (display this " + "message)\n" + "key [hmac:]keyname secret (use TSIG to sign " + "the request)\n" + "gsstsig (use GSS_TSIG to " + "sign the request)\n" + "oldgsstsig (use Microsoft's " + "GSS_TSIG to sign the request)\n" + "zone name (set the zone to be " + "updated)\n" + "class CLASS (set the zone's DNS " + "class, e.g. IN (default), CH)\n" + "check-names { on | off } (enable / disable " + "check-names)\n" + "[prereq] nxdomain name (require that this " + "name does not exist)\n" + "[prereq] yxdomain name (require that this " + "name exists)\n" + "[prereq] nxrrset .... (require that this " + "RRset does not exist)\n" + "[prereq] yxrrset .... (require that this " + "RRset exists)\n" + "[update] add .... (add the given " + "record to the zone)\n" + "[update] del[ete] .... (remove the given " + "record(s) from the zone)\n"); + return (STATUS_MORE); + } + if (strcasecmp(word, "version") == 0) { + fprintf(stdout, "nsupdate " PACKAGE_VERSION "\n"); + return (STATUS_MORE); + } + fprintf(stderr, "incorrect section name: %s\n", word); + return (STATUS_SYNTAX); +} + +static uint16_t +get_next_command(void) { + uint16_t result = STATUS_QUIT; + char cmdlinebuf[MAXCMD]; + char *cmdline = NULL, *ptr = NULL; + + isc_app_block(); + if (interactive) { + cmdline = ptr = readline("> "); + if (ptr != NULL && *ptr != 0) { + add_history(ptr); + } + } else { + cmdline = fgets(cmdlinebuf, MAXCMD, input); + } + isc_app_unblock(); + + if (cmdline != NULL) { + char *tmp = cmdline; + + /* + * Normalize input by removing any eol as readline() + * removes eol but fgets doesn't. + */ + (void)nsu_strsep(&tmp, "\r\n"); + result = do_next_command(cmdline); + } + if (ptr != NULL) { + free(ptr); + } + + return (result); +} + +static bool +user_interaction(void) { + uint16_t result = STATUS_MORE; + + ddebug("user_interaction()"); + while ((result == STATUS_MORE) || (result == STATUS_SYNTAX)) { + result = get_next_command(); + if (!interactive && result == STATUS_SYNTAX) { + fatal("syntax error"); + } + } + if (result == STATUS_SEND) { + return (true); + } + return (false); +} + +static void +done_update(void) { + isc_event_t *event = global_event; + ddebug("done_update()"); + isc_task_send(global_task, &event); +} + +static void +check_tsig_error(dns_rdataset_t *rdataset, isc_buffer_t *b) { + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_any_tsig_t tsig; + + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &tsig, NULL); + check_result(result, "dns_rdata_tostruct"); + if (tsig.error != 0) { + if (isc_buffer_remaininglength(b) < 1) { + check_result(ISC_R_NOSPACE, "isc_buffer_" + "remaininglength"); + } + isc_buffer_putstr(b, "(" /*)*/); + result = dns_tsigrcode_totext(tsig.error, b); + check_result(result, "dns_tsigrcode_totext"); + if (isc_buffer_remaininglength(b) < 1) { + check_result(ISC_R_NOSPACE, "isc_buffer_" + "remaininglength"); + } + isc_buffer_putstr(b, /*(*/ ")"); + } +} + +static bool +next_primary(const char *caller, isc_sockaddr_t *addr, isc_result_t eresult) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "; Communication with %s failed: %s\n", addrbuf, + isc_result_totext(eresult)); + if (++primary_inuse >= primary_total) { + return (false); + } + ddebug("%s: trying next server", caller); + return (true); +} + +static void +update_completed(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + isc_result_t result; + dns_request_t *request; + + UNUSED(task); + + ddebug("update_completed()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + + if (shuttingdown) { + dns_request_destroy(&request); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (reqev->result != ISC_R_SUCCESS) { + if (!next_primary("update_completed", + &primary_servers[primary_inuse], + reqev->result)) + { + seenerror = true; + goto done; + } + + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + dns_message_renderreset(updatemsg); + dns_message_settsigkey(updatemsg, NULL); + send_update(zname, &primary_servers[primary_inuse]); + isc_event_free(&event); + return; + } + + LOCK(&answer_lock); + dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &answer); + result = dns_request_getresponse(request, answer, + DNS_MESSAGEPARSE_PRESERVEORDER); + switch (result) { + case ISC_R_SUCCESS: + if (answer->verify_attempted) { + ddebug("tsig verification successful"); + } + break; + case DNS_R_CLOCKSKEW: + case DNS_R_EXPECTEDTSIG: + case DNS_R_TSIGERRORSET: + case DNS_R_TSIGVERIFYFAILURE: + case DNS_R_UNEXPECTEDTSIG: + case ISC_R_FAILURE: +#if 0 + if (usegsstsig && answer->rcode == dns_rcode_noerror) { + /* + * For MS DNS that violates RFC 2845, section 4.2 + */ + break; + } +#endif /* if 0 */ + fprintf(stderr, "; TSIG error with server: %s\n", + isc_result_totext(result)); + seenerror = true; + break; + default: + check_result(result, "dns_request_getresponse"); + } + + if (answer->opcode != dns_opcode_update) { + fatal("invalid OPCODE in response to UPDATE request"); + } + + if (answer->rcode != dns_rcode_noerror) { + seenerror = true; + if (!debugging) { + char buf[64]; + isc_buffer_t b; + dns_rdataset_t *rds; + + isc_buffer_init(&b, buf, sizeof(buf) - 1); + result = dns_rcode_totext(answer->rcode, &b); + check_result(result, "dns_rcode_totext"); + rds = dns_message_gettsig(answer, NULL); + if (rds != NULL) { + check_tsig_error(rds, &b); + } + fprintf(stderr, "update failed: %.*s\n", + (int)isc_buffer_usedlength(&b), buf); + } + } + if (debugging) { + show_message(stderr, answer, "\nReply from update query:"); + } + UNLOCK(&answer_lock); + +done: + dns_request_destroy(&request); + if (usegsstsig) { + dns_name_free(&tmpzonename, gmctx); + dns_name_free(&restart_primary, gmctx); + dns_name_init(&tmpzonename, 0); + dns_name_init(&restart_primary, 0); + } + isc_event_free(&event); + done_update(); +} + +static void +send_update(dns_name_t *zone, isc_sockaddr_t *primary) { + isc_result_t result; + dns_request_t *request = NULL; + unsigned int options = DNS_REQUESTOPT_CASE; + isc_sockaddr_t *srcaddr; + + ddebug("send_update()"); + + setzone(zone); + + if (usevc) { + options |= DNS_REQUESTOPT_TCP; + } + if (tsigkey == NULL && sig0key != NULL) { + result = dns_message_setsig0key(updatemsg, sig0key); + check_result(result, "dns_message_setsig0key"); + } + if (debugging) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(primary, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "Sending update to %s\n", addrbuf); + } + + if (isc_sockaddr_pf(primary) == AF_INET6) { + srcaddr = localaddr6; + } else { + srcaddr = localaddr4; + } + + /* Windows doesn't like the tsig name to be compressed. */ + if (updatemsg->tsigname) { + updatemsg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; + } + + result = dns_request_create(requestmgr, updatemsg, srcaddr, primary, + options, tsigkey, timeout, udp_timeout, + udp_retries, global_task, update_completed, + NULL, &request); + check_result(result, "dns_request_create"); + + if (debugging) { + show_message(stdout, updatemsg, "Outgoing update query:"); + } + + requests++; +} + +static void +next_server(const char *caller, isc_sockaddr_t *addr, isc_result_t eresult) { + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + fprintf(stderr, "; Communication with %s failed: %s\n", addrbuf, + isc_result_totext(eresult)); + if (++ns_inuse >= ns_total) { + fatal("could not reach any name server"); + } else { + ddebug("%s: trying next server", caller); + } +} + +static void +recvsoa(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + dns_request_t *request = NULL; + isc_result_t result, eresult; + dns_message_t *rcvmsg = NULL; + dns_section_t section; + dns_name_t *name = NULL; + dns_rdataset_t *soaset = NULL; + dns_rdata_soa_t soa; + dns_rdata_t soarr = DNS_RDATA_INIT; + int pass = 0; + dns_name_t primary; + nsu_requestinfo_t *reqinfo; + dns_message_t *soaquery = NULL; + isc_sockaddr_t *addr; + isc_sockaddr_t *srcaddr; + bool seencname = false; + dns_name_t tname; + unsigned int nlabels; + + UNUSED(task); + + ddebug("recvsoa()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + eresult = reqev->result; + reqinfo = reqev->ev_arg; + soaquery = reqinfo->msg; + addr = reqinfo->addr; + + if (shuttingdown) { + dns_request_destroy(&request); + dns_message_detach(&soaquery); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (eresult != ISC_R_SUCCESS) { + next_server("recvsoa", addr, eresult); + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + dns_message_renderreset(soaquery); + dns_message_settsigkey(soaquery, NULL); + sendrequest(&servers[ns_inuse], soaquery, &request); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + isc_event_free(&event); + setzoneclass(dns_rdataclass_none); + return; + } + + isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t)); + reqinfo = NULL; + isc_event_free(&event); + reqev = NULL; + + ddebug("About to create rcvmsg"); + dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + result = dns_request_getresponse(request, rcvmsg, + DNS_MESSAGEPARSE_PRESERVEORDER); + if (result == DNS_R_TSIGERRORSET && servers != NULL) { + unsigned int options = 0; + + dns_message_detach(&rcvmsg); + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + reqinfo = isc_mem_get(gmctx, sizeof(nsu_requestinfo_t)); + reqinfo->msg = soaquery; + reqinfo->addr = addr; + dns_message_renderreset(soaquery); + ddebug("retrying soa request without TSIG"); + + if (!default_servers && usevc) { + options |= DNS_REQUESTOPT_TCP; + } + + if (isc_sockaddr_pf(addr) == AF_INET6) { + srcaddr = localaddr6; + } else { + srcaddr = localaddr4; + } + + result = dns_request_create(requestmgr, soaquery, srcaddr, addr, + options, NULL, timeout, udp_timeout, + udp_retries, global_task, recvsoa, + reqinfo, &request); + check_result(result, "dns_request_create"); + requests++; + return; + } + check_result(result, "dns_request_getresponse"); + + if (rcvmsg->rcode == dns_rcode_refused) { + next_server("recvsoa", addr, DNS_R_REFUSED); + dns_message_detach(&rcvmsg); + dns_request_destroy(&request); + dns_message_renderreset(soaquery); + dns_message_settsigkey(soaquery, NULL); + sendrequest(&servers[ns_inuse], soaquery, &request); + return; + } + + section = DNS_SECTION_ANSWER; + POST(section); + if (debugging) { + show_message(stderr, rcvmsg, "Reply from SOA query:"); + } + + if (rcvmsg->opcode != dns_opcode_query) { + fatal("invalid OPCODE in response to SOA query"); + } + + if (rcvmsg->rcode != dns_rcode_noerror && + rcvmsg->rcode != dns_rcode_nxdomain) + { + fatal("response to SOA query was unsuccessful"); + } + + if (userzone != NULL && rcvmsg->rcode == dns_rcode_nxdomain) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(userzone, namebuf, sizeof(namebuf)); + error("specified zone '%s' does not exist (NXDOMAIN)", namebuf); + dns_message_detach(&rcvmsg); + dns_request_destroy(&request); + dns_message_detach(&soaquery); + ddebug("Out of recvsoa"); + seenerror = true; + done_update(); + return; + } + +lookforsoa: + if (pass == 0) { + section = DNS_SECTION_ANSWER; + } else if (pass == 1) { + section = DNS_SECTION_AUTHORITY; + } else { + goto droplabel; + } + + result = dns_message_firstname(rcvmsg, section); + if (result != ISC_R_SUCCESS) { + pass++; + goto lookforsoa; + } + while (result == ISC_R_SUCCESS) { + name = NULL; + dns_message_currentname(rcvmsg, section, &name); + soaset = NULL; + result = dns_message_findtype(name, dns_rdatatype_soa, 0, + &soaset); + if (result == ISC_R_SUCCESS) { + break; + } + if (section == DNS_SECTION_ANSWER) { + dns_rdataset_t *tset = NULL; + if (dns_message_findtype(name, dns_rdatatype_cname, 0, + &tset) == ISC_R_SUCCESS || + dns_message_findtype(name, dns_rdatatype_dname, 0, + &tset) == ISC_R_SUCCESS) + { + seencname = true; + break; + } + } + + result = dns_message_nextname(rcvmsg, section); + } + + if (soaset == NULL && !seencname) { + pass++; + goto lookforsoa; + } + + if (seencname) { + goto droplabel; + } + + if (debugging) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(name, namestr, sizeof(namestr)); + fprintf(stderr, "Found zone name: %s\n", namestr); + } + + result = dns_rdataset_first(soaset); + check_result(result, "dns_rdataset_first"); + + dns_rdata_init(&soarr); + dns_rdataset_current(soaset, &soarr); + result = dns_rdata_tostruct(&soarr, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + + dns_name_init(&primary, NULL); + dns_name_clone(&soa.origin, &primary); + + if (userzone != NULL) { + zname = userzone; + } else { + /* + * Save the zone name in case we need to try a second + * address. + */ + zname = dns_fixedname_initname(&fzname); + dns_name_copy(name, zname); + } + + if (debugging) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(&primary, namestr, sizeof(namestr)); + fprintf(stderr, "The primary is: %s\n", namestr); + } + + if (default_servers) { + char serverstr[DNS_NAME_MAXTEXT + 1]; + isc_buffer_t buf; + size_t size; + + isc_buffer_init(&buf, serverstr, sizeof(serverstr)); + result = dns_name_totext(&primary, true, &buf); + check_result(result, "dns_name_totext"); + serverstr[isc_buffer_usedlength(&buf)] = 0; + + if (primary_servers != NULL && primary_servers != servers) { + isc_mem_put(gmctx, primary_servers, + primary_alloc * sizeof(isc_sockaddr_t)); + } + primary_alloc = MAX_SERVERADDRS; + size = primary_alloc * sizeof(isc_sockaddr_t); + primary_servers = isc_mem_get(gmctx, size); + + memset(primary_servers, 0, size); + primary_total = get_addresses(serverstr, dnsport, + primary_servers, primary_alloc); + if (primary_total == 0) { + seenerror = true; + dns_rdata_freestruct(&soa); + dns_message_detach(&soaquery); + dns_request_destroy(&request); + dns_message_detach(&rcvmsg); + ddebug("Out of recvsoa"); + done_update(); + return; + } + primary_inuse = 0; + } else { + primary_from_servers(); + } + dns_rdata_freestruct(&soa); + +#if HAVE_GSSAPI + if (usegsstsig) { + dns_name_init(&tmpzonename, NULL); + dns_name_dup(zname, gmctx, &tmpzonename); + dns_name_init(&restart_primary, NULL); + dns_name_dup(&primary, gmctx, &restart_primary); + start_gssrequest(&primary); + } else { + send_update(zname, &primary_servers[primary_inuse]); + setzoneclass(dns_rdataclass_none); + } +#else /* HAVE_GSSAPI */ + send_update(zname, &primary_servers[primary_inuse]); + setzoneclass(dns_rdataclass_none); +#endif /* HAVE_GSSAPI */ + + dns_message_detach(&soaquery); + dns_request_destroy(&request); + +out: + dns_message_detach(&rcvmsg); + ddebug("Out of recvsoa"); + return; + +droplabel: + result = dns_message_firstname(soaquery, DNS_SECTION_QUESTION); + INSIST(result == ISC_R_SUCCESS); + name = NULL; + dns_message_currentname(soaquery, DNS_SECTION_QUESTION, &name); + nlabels = dns_name_countlabels(name); + if (nlabels == 1) { + fatal("could not find enclosing zone"); + } + dns_name_init(&tname, NULL); + dns_name_getlabelsequence(name, 1, nlabels - 1, &tname); + dns_name_clone(&tname, name); + dns_request_destroy(&request); + dns_message_renderreset(soaquery); + dns_message_settsigkey(soaquery, NULL); + sendrequest(&servers[ns_inuse], soaquery, &request); + goto out; +} + +static void +sendrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request) { + isc_result_t result; + nsu_requestinfo_t *reqinfo; + isc_sockaddr_t *srcaddr; + unsigned int options = 0; + + if (!default_servers && usevc) { + options |= DNS_REQUESTOPT_TCP; + } + + reqinfo = isc_mem_get(gmctx, sizeof(nsu_requestinfo_t)); + reqinfo->msg = msg; + reqinfo->addr = destaddr; + + if (isc_sockaddr_pf(destaddr) == AF_INET6) { + srcaddr = localaddr6; + } else { + srcaddr = localaddr4; + } + + result = dns_request_create(requestmgr, msg, srcaddr, destaddr, options, + default_servers ? NULL : tsigkey, timeout, + udp_timeout, udp_retries, global_task, + recvsoa, reqinfo, request); + check_result(result, "dns_request_create"); + requests++; +} + +#if HAVE_GSSAPI + +/* + * Get the realm from the users kerberos ticket if possible + */ +static void +get_ticket_realm(isc_mem_t *mctx) { + krb5_context ctx; + krb5_error_code rc; + krb5_ccache ccache; + krb5_principal princ; + char *name; + const char *ticket_realm; + + rc = krb5_init_context(&ctx); + if (rc != 0) { + return; + } + + rc = krb5_cc_default(ctx, &ccache); + if (rc != 0) { + krb5_free_context(ctx); + return; + } + + rc = krb5_cc_get_principal(ctx, ccache, &princ); + if (rc != 0) { + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + return; + } + + rc = krb5_unparse_name(ctx, princ, &name); + if (rc != 0) { + krb5_free_principal(ctx, princ); + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + return; + } + + ticket_realm = strrchr(name, '@'); + if (ticket_realm != NULL) { + realm = isc_mem_strdup(mctx, ticket_realm); + } + + free(name); + krb5_free_principal(ctx, princ); + krb5_cc_close(ctx, ccache); + krb5_free_context(ctx); + if (realm != NULL && debugging) { + fprintf(stderr, "Found realm from ticket: %s\n", realm + 1); + } +} + +static void +failed_gssrequest(void) { + seenerror = true; + + dns_name_free(&tmpzonename, gmctx); + dns_name_free(&restart_primary, gmctx); + dns_name_init(&tmpzonename, NULL); + dns_name_init(&restart_primary, NULL); + + done_update(); +} + +static void +start_gssrequest(dns_name_t *primary) { + dns_gss_ctx_id_t context; + isc_buffer_t buf; + isc_result_t result; + uint32_t val = 0; + dns_message_t *rmsg = NULL; + dns_request_t *request = NULL; + dns_name_t *servname; + dns_fixedname_t fname; + char namestr[DNS_NAME_FORMATSIZE]; + char mykeystr[DNS_NAME_FORMATSIZE]; + char *err_message = NULL; + + debug("start_gssrequest"); + usevc = true; + + if (gssring != NULL) { + dns_tsigkeyring_detach(&gssring); + } + gssring = NULL; + result = dns_tsigkeyring_create(gmctx, &gssring); + + if (result != ISC_R_SUCCESS) { + fatal("dns_tsigkeyring_create failed: %s", + isc_result_totext(result)); + } + + dns_name_format(primary, namestr, sizeof(namestr)); + if (kserver == NULL) { + kserver = isc_mem_get(gmctx, sizeof(isc_sockaddr_t)); + } + + memmove(kserver, &primary_servers[primary_inuse], + sizeof(isc_sockaddr_t)); + + servname = dns_fixedname_initname(&fname); + + if (realm == NULL) { + get_ticket_realm(gmctx); + } + + result = snprintf(servicename, sizeof(servicename), "DNS/%s%s", namestr, + realm ? realm : ""); + RUNTIME_CHECK(result < sizeof(servicename)); + isc_buffer_init(&buf, servicename, strlen(servicename)); + isc_buffer_add(&buf, strlen(servicename)); + result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fatal("dns_name_fromtext(servname) failed: %s", + isc_result_totext(result)); + } + + keyname = dns_fixedname_initname(&fkname); + + isc_nonce_buf(&val, sizeof(val)); + + result = snprintf(mykeystr, sizeof(mykeystr), "%u.sig-%s", val, + namestr); + RUNTIME_CHECK(result <= sizeof(mykeystr)); + + isc_buffer_init(&buf, mykeystr, strlen(mykeystr)); + isc_buffer_add(&buf, strlen(mykeystr)); + + result = dns_name_fromtext(keyname, &buf, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + fatal("dns_name_fromtext(keyname) failed: %s", + isc_result_totext(result)); + } + + /* Windows doesn't recognize name compression in the key name. */ + keyname->attributes |= DNS_NAMEATTR_NOCOMPRESS; + + rmsg = NULL; + dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &rmsg); + + /* Build first request. */ + context = GSS_C_NO_CONTEXT; + result = dns_tkey_buildgssquery(rmsg, keyname, servname, NULL, 0, + &context, use_win2k_gsstsig, gmctx, + &err_message); + if (result == ISC_R_FAILURE) { + fprintf(stderr, "tkey query failed: %s\n", + err_message != NULL ? err_message : "unknown error"); + goto failure; + } + if (result != ISC_R_SUCCESS) { + fatal("dns_tkey_buildgssquery failed: %s", + isc_result_totext(result)); + } + + send_gssrequest(kserver, rmsg, &request, context); + return; + +failure: + if (rmsg != NULL) { + dns_message_detach(&rmsg); + } + if (err_message != NULL) { + isc_mem_free(gmctx, err_message); + } + failed_gssrequest(); +} + +static void +send_gssrequest(isc_sockaddr_t *destaddr, dns_message_t *msg, + dns_request_t **request, gss_ctx_id_t context) { + isc_result_t result; + nsu_gssinfo_t *reqinfo; + unsigned int options = 0; + isc_sockaddr_t *srcaddr; + + debug("send_gssrequest"); + REQUIRE(destaddr != NULL); + + reqinfo = isc_mem_get(gmctx, sizeof(nsu_gssinfo_t)); + reqinfo->msg = msg; + reqinfo->addr = destaddr; + reqinfo->context = context; + + options |= DNS_REQUESTOPT_TCP; + + if (isc_sockaddr_pf(destaddr) == AF_INET6) { + srcaddr = localaddr6; + } else { + srcaddr = localaddr4; + } + + result = dns_request_create(requestmgr, msg, srcaddr, destaddr, options, + tsigkey, timeout, udp_timeout, udp_retries, + global_task, recvgss, reqinfo, request); + check_result(result, "dns_request_create"); + if (debugging) { + show_message(stdout, msg, "Outgoing update query:"); + } + requests++; +} + +static void +recvgss(isc_task_t *task, isc_event_t *event) { + dns_requestevent_t *reqev = NULL; + dns_request_t *request = NULL; + isc_result_t result, eresult; + dns_message_t *rcvmsg = NULL; + nsu_gssinfo_t *reqinfo; + dns_message_t *tsigquery = NULL; + isc_sockaddr_t *addr; + dns_gss_ctx_id_t context; + isc_buffer_t buf; + dns_name_t *servname; + dns_fixedname_t fname; + char *err_message = NULL; + + UNUSED(task); + + ddebug("recvgss()"); + + requests--; + + REQUIRE(event->ev_type == DNS_EVENT_REQUESTDONE); + reqev = (dns_requestevent_t *)event; + request = reqev->request; + eresult = reqev->result; + reqinfo = reqev->ev_arg; + tsigquery = reqinfo->msg; + context = reqinfo->context; + addr = reqinfo->addr; + + if (shuttingdown) { + dns_request_destroy(&request); + dns_message_detach(&tsigquery); + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + isc_event_free(&event); + maybeshutdown(); + return; + } + + if (eresult != ISC_R_SUCCESS) { + ddebug("Destroying request [%p]", request); + dns_request_destroy(&request); + if (!next_primary("recvgss", addr, eresult)) { + dns_message_detach(&tsigquery); + failed_gssrequest(); + } else { + dns_message_renderreset(tsigquery); + memmove(kserver, &primary_servers[primary_inuse], + sizeof(isc_sockaddr_t)); + send_gssrequest(kserver, tsigquery, &request, context); + } + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + isc_event_free(&event); + return; + } + isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t)); + + isc_event_free(&event); + reqev = NULL; + + ddebug("recvgss creating rcvmsg"); + dns_message_create(gmctx, DNS_MESSAGE_INTENTPARSE, &rcvmsg); + + result = dns_request_getresponse(request, rcvmsg, + DNS_MESSAGEPARSE_PRESERVEORDER); + check_result(result, "dns_request_getresponse"); + + if (debugging) { + show_message(stderr, rcvmsg, + "recvmsg reply from GSS-TSIG query"); + } + + if (rcvmsg->opcode != dns_opcode_query) { + fatal("invalid OPCODE in response to GSS-TSIG query"); + } + + if (rcvmsg->rcode == dns_rcode_formerr && !tried_other_gsstsig) { + ddebug("recvgss trying %s GSS-TSIG", + use_win2k_gsstsig ? "Standard" : "Win2k"); + if (use_win2k_gsstsig) { + use_win2k_gsstsig = false; + } else { + use_win2k_gsstsig = true; + } + tried_other_gsstsig = true; + start_gssrequest(&restart_primary); + goto done; + } + + if (rcvmsg->rcode != dns_rcode_noerror && + rcvmsg->rcode != dns_rcode_nxdomain) + { + char rcode[64]; + isc_buffer_t b; + + isc_buffer_init(&b, rcode, sizeof(rcode) - 1); + result = dns_rcode_totext(rcvmsg->rcode, &b); + check_result(result, "dns_rcode_totext"); + rcode[isc_buffer_usedlength(&b)] = 0; + + fatal("response to GSS-TSIG query was unsuccessful (%s)", + rcode); + } + + servname = dns_fixedname_initname(&fname); + isc_buffer_init(&buf, servicename, strlen(servicename)); + isc_buffer_add(&buf, strlen(servicename)); + result = dns_name_fromtext(servname, &buf, dns_rootname, 0, NULL); + check_result(result, "dns_name_fromtext"); + + tsigkey = NULL; + result = dns_tkey_gssnegotiate(tsigquery, rcvmsg, servname, &context, + &tsigkey, gssring, use_win2k_gsstsig, + &err_message); + switch (result) { + case DNS_R_CONTINUE: + dns_message_detach(&rcvmsg); + dns_request_destroy(&request); + send_gssrequest(kserver, tsigquery, &request, context); + ddebug("Out of recvgss"); + return; + + case ISC_R_SUCCESS: + /* + * XXXSRA Waaay too much fun here. There's no good + * reason why we need a TSIG here (the people who put + * it into the spec admitted at the time that it was + * not a security issue), and Windows clients don't + * seem to work if named complies with the spec and + * includes the gratuitous TSIG. So we're in the + * bizarre situation of having to choose between + * complying with a useless requirement in the spec + * and interoperating. This is nuts. If we can + * confirm this behavior, we should ask the WG to + * consider removing the requirement for the + * gratuitous TSIG here. For the moment, we ignore + * the TSIG -- this too is a spec violation, but it's + * the least insane thing to do. + */ +#if 0 + /* + * Verify the signature. + */ + rcvmsg->state = DNS_SECTION_ANY; + dns_message_setquerytsig(rcvmsg, NULL); + result = dns_message_settsigkey(rcvmsg, tsigkey); + check_result(result, "dns_message_settsigkey"); + result = dns_message_checksig(rcvmsg, NULL); + ddebug("tsig verification: %s", isc_result_totext(result)); + check_result(result, "dns_message_checksig"); +#endif /* 0 */ + + send_update(&tmpzonename, &primary_servers[primary_inuse]); + setzoneclass(dns_rdataclass_none); + break; + + default: + fatal("dns_tkey_gssnegotiate: %s %s", isc_result_totext(result), + err_message != NULL ? err_message : ""); + } + +done: + dns_request_destroy(&request); + dns_message_detach(&tsigquery); + + dns_message_detach(&rcvmsg); + ddebug("Out of recvgss"); +} +#endif /* HAVE_GSSAPI */ + +static void +start_update(void) { + isc_result_t result; + dns_rdataset_t *rdataset = NULL; + dns_name_t *name = NULL; + dns_request_t *request = NULL; + dns_message_t *soaquery = NULL; + dns_name_t *firstname; + dns_section_t section = DNS_SECTION_UPDATE; + + ddebug("start_update()"); + + LOCK(&answer_lock); + if (answer != NULL) { + dns_message_detach(&answer); + } + UNLOCK(&answer_lock); + + /* + * If we have both the zone and the servers we have enough information + * to send the update straight away otherwise we need to discover + * the zone and / or the primary server. + */ + if (userzone != NULL && !default_servers && !usegsstsig) { + primary_from_servers(); + send_update(userzone, &primary_servers[primary_inuse]); + setzoneclass(dns_rdataclass_none); + return; + } + + dns_message_create(gmctx, DNS_MESSAGE_INTENTRENDER, &soaquery); + + if (default_servers) { + soaquery->flags |= DNS_MESSAGEFLAG_RD; + } + + result = dns_message_gettempname(soaquery, &name); + check_result(result, "dns_message_gettempname"); + + result = dns_message_gettemprdataset(soaquery, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + + dns_rdataset_makequestion(rdataset, getzoneclass(), dns_rdatatype_soa); + + if (userzone != NULL) { + dns_name_clone(userzone, name); + } else { + dns_rdataset_t *tmprdataset; + result = dns_message_firstname(updatemsg, section); + if (result == ISC_R_NOMORE) { + section = DNS_SECTION_PREREQUISITE; + result = dns_message_firstname(updatemsg, section); + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(soaquery, &name); + dns_rdataset_disassociate(rdataset); + dns_message_puttemprdataset(soaquery, &rdataset); + dns_message_detach(&soaquery); + done_update(); + return; + } + firstname = NULL; + dns_message_currentname(updatemsg, section, &firstname); + dns_name_clone(firstname, name); + /* + * Looks to see if the first name references a DS record + * and if that name is not the root remove a label as DS + * records live in the parent zone so we need to start our + * search one label up. + */ + tmprdataset = ISC_LIST_HEAD(firstname->list); + if (section == DNS_SECTION_UPDATE && + !dns_name_equal(firstname, dns_rootname) && + tmprdataset->type == dns_rdatatype_ds) + { + unsigned int labels = dns_name_countlabels(name); + dns_name_getlabelsequence(name, 1, labels - 1, name); + } + } + + ISC_LIST_INIT(name->list); + ISC_LIST_APPEND(name->list, rdataset, link); + dns_message_addname(soaquery, name, DNS_SECTION_QUESTION); + + ns_inuse = 0; + sendrequest(&servers[ns_inuse], soaquery, &request); +} + +static void +cleanup(void) { + ddebug("cleanup()"); + + LOCK(&answer_lock); + if (answer != NULL) { + dns_message_detach(&answer); + } + UNLOCK(&answer_lock); + + ddebug("Shutting down managers"); + isc_managers_destroy(&netmgr, &taskmgr, NULL); + +#if HAVE_GSSAPI + if (tsigkey != NULL) { + ddebug("detach tsigkey x%p", tsigkey); + dns_tsigkey_detach(&tsigkey); + } + if (gssring != NULL) { + ddebug("Detaching GSS-TSIG keyring"); + dns_tsigkeyring_detach(&gssring); + } +#endif /* ifdef HAVE_GSSAPI */ + + if (sig0key != NULL) { + dst_key_free(&sig0key); + } + + ddebug("Destroying event"); + isc_event_free(&global_event); + +#ifdef HAVE_GSSAPI + /* + * Cleanup GSSAPI resources after taskmgr has been destroyed. + */ + if (kserver != NULL) { + isc_mem_put(gmctx, kserver, sizeof(isc_sockaddr_t)); + kserver = NULL; + } + if (realm != NULL) { + isc_mem_free(gmctx, realm); + realm = NULL; + } + if (dns_name_dynamic(&tmpzonename)) { + dns_name_free(&tmpzonename, gmctx); + } + if (dns_name_dynamic(&restart_primary)) { + dns_name_free(&restart_primary, gmctx); + } +#endif /* ifdef HAVE_GSSAPI */ + + ddebug("Removing log context"); + isc_log_destroy(&glctx); + + ddebug("Destroying memory context"); + if (memdebugging) { + isc_mem_stats(gmctx, stderr); + } + isc_mem_destroy(&gmctx); + + isc_mutex_destroy(&answer_lock); + + if (is_dst_up) { + ddebug("Destroy DST lib"); + dst_lib_destroy(); + is_dst_up = false; + } +} + +static void +getinput(isc_task_t *task, isc_event_t *event) { + bool more; + + UNUSED(task); + + if (shuttingdown) { + maybeshutdown(); + return; + } + + if (global_event == NULL) { + global_event = event; + } + + reset_system(); + more = user_interaction(); + if (!more) { + isc_app_shutdown(); + return; + } + start_update(); + return; +} + +int +main(int argc, char **argv) { + isc_result_t result; + uint32_t timeoutms; + + style = &dns_master_style_debug; + + input = stdin; + + interactive = isatty(0); + + isc_app_start(); + + if (isc_net_probeipv4() == ISC_R_SUCCESS) { + have_ipv4 = true; + } + if (isc_net_probeipv6() == ISC_R_SUCCESS) { + have_ipv6 = true; + } + if (!have_ipv4 && !have_ipv6) { + fatal("could not find either IPv4 or IPv6"); + } + + pre_parse_args(argc, argv); + + isc_mem_create(&gmctx); + + parse_args(argc, argv); + + setup_system(); + + /* Set the network manager timeouts in milliseconds. */ + timeoutms = timeout * 1000; + isc_nm_settimeouts(netmgr, timeoutms, timeoutms, timeoutms, timeoutms); + + result = isc_app_onrun(gmctx, global_task, getinput, NULL); + check_result(result, "isc_app_onrun"); + + (void)isc_app_run(); + + cleanup(); + + isc_app_finish(); + + if (seenerror) { + return (2); + } else { + return (0); + } +} diff --git a/bin/nsupdate/nsupdate.rst b/bin/nsupdate/nsupdate.rst new file mode 100644 index 0000000..81bb481 --- /dev/null +++ b/bin/nsupdate/nsupdate.rst @@ -0,0 +1,391 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. iscman:: nsupdate +.. program:: nsupdate +.. _man_nsupdate: + +nsupdate - dynamic DNS update utility +------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`nsupdate` [**-d**] [**-D**] [**-i**] [**-L** level] [ [**-g**] | [**-o**] | [**-l**] | [**-y** [hmac:]keyname:secret] | [**-k** keyfile] ] [**-t** timeout] [**-u** udptimeout] [**-r** udpretries] [**-v**] [**-T**] [**-P**] [**-V**] [ [**-4**] | [**-6**] ] [filename] + +Description +~~~~~~~~~~~ + +:program:`nsupdate` is used to submit Dynamic DNS Update requests, as defined in +:rfc:`2136`, to a name server. This allows resource records to be added or +removed from a zone without manually editing the zone file. A single +update request can contain requests to add or remove more than one +resource record. + +Zones that are under dynamic control via :program:`nsupdate` or a DHCP server +should not be edited by hand. Manual edits could conflict with dynamic +updates and cause data to be lost. + +The resource records that are dynamically added or removed with +:program:`nsupdate` must be in the same zone. Requests are sent to the +zone's primary server, which is identified by the MNAME field of the +zone's SOA record. + +Transaction signatures can be used to authenticate the Dynamic DNS +updates. These use the TSIG resource record type described in :rfc:`2845`, +the SIG(0) record described in :rfc:`2535` and :rfc:`2931`, or GSS-TSIG as +described in :rfc:`3645`. + +TSIG relies on a shared secret that should only be known to :program:`nsupdate` +and the name server. For instance, suitable ``key`` and ``server`` +statements are added to |named_conf| so that the name server +can associate the appropriate secret key and algorithm with the IP +address of the client application that is using TSIG +authentication. :iscman:`ddns-confgen` can generate suitable +configuration fragments. :program:`nsupdate` uses the :option:`-y` or :option:`-k` options +to provide the TSIG shared secret; these options are mutually exclusive. + +SIG(0) uses public key cryptography. To use a SIG(0) key, the public key +must be stored in a KEY record in a zone served by the name server. + +GSS-TSIG uses Kerberos credentials. Standard GSS-TSIG mode is switched +on with the :option:`-g` flag. A non-standards-compliant variant of GSS-TSIG +used by Windows 2000 can be switched on with the :option:`-o` flag. + +Options +~~~~~~~ + +.. option:: -4 + + This option sets use of IPv4 only. + +.. option:: -6 + + This option sets use of IPv6 only. + +.. option:: -C + + Overrides the default `resolv.conf` file. This is only intended for testing. + +.. option:: -d + + This option sets debug mode, which provides tracing information about the update + requests that are made and the replies received from the name server. + +.. option:: -D + + This option sets extra debug mode. + +.. option:: -g + + This option enables standard GSS-TSIG mode. + +.. option:: -i + + This option forces interactive mode, even when standard input is not a terminal. + +.. option:: -k keyfile + + This option indicates the file containing the TSIG authentication key. Keyfiles may be in + two formats: a single file containing a :iscman:`named.conf`-format ``key`` + statement, which may be generated automatically by :iscman:`ddns-confgen`; + or a pair of files whose names are of the format + ``K{name}.+157.+{random}.key`` and + ``K{name}.+157.+{random}.private``, which can be generated by + :iscman:`dnssec-keygen`. The :option:`-k` option can also be used to specify a SIG(0) + key used to authenticate Dynamic DNS update requests. In this case, + the key specified is not an HMAC-MD5 key. + +.. option:: -l + + This option sets local-host only mode, which sets the server address to localhost + (disabling the ``server`` so that the server address cannot be + overridden). Connections to the local server use a TSIG key + found in |session_key|, which is automatically + generated by :iscman:`named` if any local ``primary`` zone has set + ``update-policy`` to ``local``. The location of this key file can be + overridden with the :option:`-k` option. + +.. option:: -L level + + This option sets the logging debug level. If zero, logging is disabled. + +.. option:: -o + + This option enables a non-standards-compliant variant of GSS-TSIG + used by Windows 2000. + +.. option:: -p port + + This option sets the port to use for connections to a name server. The default is + 53. + +.. option:: -P + + This option prints the list of private BIND-specific resource record types whose + format is understood by :program:`nsupdate`. See also the :option:`-T` option. + +.. option:: -r udpretries + + This option sets the number of UDP retries. The default is 3. If zero, only one update + request is made. + +.. option:: -t timeout + + This option sets the maximum time an update request can take before it is aborted. The + default is 300 seconds. If zero, the timeout is disabled for TCP mode. For UDP mode, + the option :option:`-u` takes precedence over this option, unless the option :option:`-u` + is set to zero, in which case the interval is computed from the :option:`-t` timeout interval + and the number of UDP retries. For UDP mode, the timeout can not be disabled, and will + be rounded up to 1 second in case if both :option:`-t` and :option:`-u` are set to zero. + +.. option:: -T + + This option prints the list of IANA standard resource record types whose format is + understood by :program:`nsupdate`. :program:`nsupdate` exits after the lists + are printed. The :option:`-T` option can be combined with the :option:`-P` + option. + + Other types can be entered using ``TYPEXXXXX`` where ``XXXXX`` is the + decimal value of the type with no leading zeros. The rdata, if + present, is parsed using the UNKNOWN rdata format, ( + ). + +.. option:: -u udptimeout + + This option sets the UDP retry interval. The default is 3 seconds. If zero, the + interval is computed from the timeout interval and number of UDP + retries. + +.. option:: -v + + This option specifies that TCP should be used even for small update requests. By default, :program:`nsupdate` uses + UDP to send update requests to the name server unless they are too + large to fit in a UDP request, in which case TCP is used. TCP may + be preferable when a batch of update requests is made. + +.. option:: -V + + This option prints the version number and exits. + +.. option:: -y [hmac:]keyname:secret + + This option sets the literal TSIG authentication key. ``keyname`` is the name of the key, + and ``secret`` is the base64 encoded shared secret. ``hmac`` is the + name of the key algorithm; valid choices are ``hmac-md5``, + ``hmac-sha1``, ``hmac-sha224``, ``hmac-sha256``, ``hmac-sha384``, or + ``hmac-sha512``. If ``hmac`` is not specified, the default is + ``hmac-md5``, or if MD5 was disabled, ``hmac-sha256``. + + NOTE: Use of the :option:`-y` option is discouraged because the shared + secret is supplied as a command-line argument in clear text. This may + be visible in the output from ps1 or in a history file maintained by + the user's shell. + +Input Format +~~~~~~~~~~~~ + +:program:`nsupdate` reads input from ``filename`` or standard input. Each +command is supplied on exactly one line of input. Some commands are for +administrative purposes; others are either update instructions or +prerequisite checks on the contents of the zone. These checks set +conditions that some name or set of resource records (RRset) either +exists or is absent from the zone. These conditions must be met if the +entire update request is to succeed. Updates are rejected if the +tests for the prerequisite conditions fail. + +Every update request consists of zero or more prerequisites and zero or +more updates. This allows a suitably authenticated update request to +proceed if some specified resource records are either present or missing from +the zone. A blank input line (or the ``send`` command) causes the +accumulated commands to be sent as one Dynamic DNS update request to the +name server. + +The command formats and their meanings are as follows: + +``server servername port`` + This command sends all dynamic update requests to the name server ``servername``. + When no server statement is provided, :program:`nsupdate` sends updates + to the primary server of the correct zone. The MNAME field of that + zone's SOA record identify the primary server for that zone. + ``port`` is the port number on ``servername`` where the dynamic + update requests are sent. If no port number is specified, the default + DNS port number of 53 is used. + + .. note:: This command has no effect when GSS-TSIG is in use. + +``local address port`` + This command sends all dynamic update requests using the local ``address``. When + no local statement is provided, :program:`nsupdate` sends updates using + an address and port chosen by the system. ``port`` can also + be used to force requests to come from a specific port. If no port number + is specified, the system assigns one. + +``zone zonename`` + This command specifies that all updates are to be made to the zone ``zonename``. + If no ``zone`` statement is provided, :program:`nsupdate` attempts to + determine the correct zone to update based on the rest of the input. + +``class classname`` + This command specifies the default class. If no ``class`` is specified, the default + class is ``IN``. + +``ttl seconds`` + This command specifies the default time-to-live, in seconds, for records to be added. The value + ``none`` clears the default TTL. + +``key hmac:keyname secret`` + This command specifies that all updates are to be TSIG-signed using the + ``keyname``-``secret`` pair. If ``hmac`` is specified, it sets + the signing algorithm in use. The default is ``hmac-md5``; if MD5 + was disabled, the default is ``hmac-sha256``. The ``key`` command overrides any key + specified on the command line via :option:`-y` or :option:`-k`. + +``gsstsig`` + This command uses GSS-TSIG to sign the updates. This is equivalent to specifying + :option:`-g` on the command line. + +``oldgsstsig`` + This command uses the Windows 2000 version of GSS-TSIG to sign the updates. This is + equivalent to specifying :option:`-o` on the command line. + +``realm [realm_name]`` + When using GSS-TSIG, this command specifies the use of ``realm_name`` rather than the default realm + in ``krb5.conf``. If no realm is specified, the saved realm is + cleared. + +``check-names [boolean]`` + This command turns on or off check-names processing on records to be added. + Check-names has no effect on prerequisites or records to be deleted. + By default check-names processing is on. If check-names processing + fails, the record is not added to the UPDATE message. + +``prereq nxdomain domain-name`` + This command requires that no resource record of any type exist with the name + ``domain-name``. + +``prereq yxdomain domain-name`` + This command requires that ``domain-name`` exist (as at least one resource + record, of any type). + +``prereq nxrrset domain-name class type`` + This command requires that no resource record exist of the specified ``type``, + ``class``, and ``domain-name``. If ``class`` is omitted, IN (Internet) + is assumed. + +``prereq yxrrset domain-name class type`` + This command requires that a resource record of the specified ``type``, + ``class`` and ``domain-name`` exist. If ``class`` is omitted, IN + (internet) is assumed. + +``prereq yxrrset domain-name class type data`` + With this command, the ``data`` from each set of prerequisites of this form sharing a + common ``type``, ``class``, and ``domain-name`` are combined to form + a set of RRs. This set of RRs must exactly match the set of RRs + existing in the zone at the given ``type``, ``class``, and + ``domain-name``. The ``data`` are written in the standard text + representation of the resource record's RDATA. + +``update delete domain-name ttl class type data`` + This command deletes any resource records named ``domain-name``. If ``type`` and + ``data`` are provided, only matching resource records are removed. + The Internet class is assumed if ``class`` is not supplied. The + ``ttl`` is ignored, and is only allowed for compatibility. + +``update add domain-name ttl class type data`` + This command adds a new resource record with the specified ``ttl``, ``class``, and + ``data``. + +``show`` + This command displays the current message, containing all of the prerequisites and + updates specified since the last send. + +``send`` + This command sends the current message. This is equivalent to entering a blank + line. + +``answer`` + This command displays the answer. + +``debug`` + This command turns on debugging. + +``version`` + This command prints the version number. + +``help`` + This command prints a list of commands. + +Lines beginning with a semicolon (;) are comments and are ignored. + +Examples +~~~~~~~~ + +The examples below show how :program:`nsupdate` can be used to insert and +delete resource records from the ``example.com`` zone. Notice that the +input in each example contains a trailing blank line, so that a group of +commands is sent as one dynamic update request to the primary name +server for ``example.com``. + +:: + + # nsupdate + > update delete oldhost.example.com A + > update add newhost.example.com 86400 A 172.16.1.1 + > send + +Any A records for ``oldhost.example.com`` are deleted, and an A record +for ``newhost.example.com`` with IP address 172.16.1.1 is added. The +newly added record has a TTL of 1 day (86400 seconds). + +:: + + # nsupdate + > prereq nxdomain nickname.example.com + > update add nickname.example.com 86400 CNAME somehost.example.com + > send + +The prerequisite condition tells the name server to verify that there are +no resource records of any type for ``nickname.example.com``. If there +are, the update request fails. If this name does not exist, a CNAME for +it is added. This ensures that when the CNAME is added, it cannot +conflict with the long-standing rule in :rfc:`1034` that a name must not +exist as any other record type if it exists as a CNAME. (The rule has +been updated for DNSSEC in :rfc:`2535` to allow CNAMEs to have RRSIG, +DNSKEY, and NSEC records.) + +Files +~~~~~ + +``/etc/resolv.conf`` + Used to identify the default name server + +|session_key| + Sets the default TSIG key for use in local-only mode + +``K{name}.+157.+{random}.key`` + Base-64 encoding of the HMAC-MD5 key created by :iscman:`dnssec-keygen`. + +``K{name}.+157.+{random}.private`` + Base-64 encoding of the HMAC-MD5 key created by :iscman:`dnssec-keygen`. + +See Also +~~~~~~~~ + +:rfc:`2136`, :rfc:`3007`, :rfc:`2104`, :rfc:`2845`, :rfc:`1034`, :rfc:`2535`, :rfc:`2931`, +:iscman:`named(8) `, :iscman:`dnssec-keygen(8) `, :iscman:`tsig-keygen(8) `. + +Bugs +~~~~ + +The TSIG key is redundantly stored in two separate files. This is a +consequence of :program:`nsupdate` using the DST library for its cryptographic +operations, and may change in future releases. diff --git a/bin/plugins/Makefile.am b/bin/plugins/Makefile.am new file mode 100644 index 0000000..611dd1b --- /dev/null +++ b/bin/plugins/Makefile.am @@ -0,0 +1,15 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) + +pkglib_LTLIBRARIES = filter-aaaa.la +pkglib_LTLIBRARIES += filter-a.la + +filter_aaaa_la_SOURCES = filter-aaaa.c +filter_a_la_SOURCES = filter-a.c +filter_aaaa_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +filter_a_la_LDFLAGS = -avoid-version -module -shared -export-dynamic diff --git a/bin/plugins/Makefile.in b/bin/plugins/Makefile.in new file mode 100644 index 0000000..b1d1bb8 --- /dev/null +++ b/bin/plugins/Makefile.in @@ -0,0 +1,847 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +subdir = bin/plugins +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkglibdir)" +LTLIBRARIES = $(pkglib_LTLIBRARIES) +filter_a_la_LIBADD = +am_filter_a_la_OBJECTS = filter-a.lo +filter_a_la_OBJECTS = $(am_filter_a_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +filter_a_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(filter_a_la_LDFLAGS) $(LDFLAGS) -o $@ +filter_aaaa_la_LIBADD = +am_filter_aaaa_la_OBJECTS = filter-aaaa.lo +filter_aaaa_la_OBJECTS = $(am_filter_aaaa_la_OBJECTS) +filter_aaaa_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(filter_aaaa_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/filter-a.Plo \ + ./$(DEPDIR)/filter-aaaa.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(filter_a_la_SOURCES) $(filter_aaaa_la_SOURCES) +DIST_SOURCES = $(filter_a_la_SOURCES) $(filter_aaaa_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBNS_CFLAGS) $(LIBISCCFG_CFLAGS) +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +pkglib_LTLIBRARIES = filter-aaaa.la filter-a.la +filter_aaaa_la_SOURCES = filter-aaaa.c +filter_a_la_SOURCES = filter-a.c +filter_aaaa_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +filter_a_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/plugins/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/plugins/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ + } + +uninstall-pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ + done + +clean-pkglibLTLIBRARIES: + -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) + @list='$(pkglib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +filter-a.la: $(filter_a_la_OBJECTS) $(filter_a_la_DEPENDENCIES) $(EXTRA_filter_a_la_DEPENDENCIES) + $(AM_V_CCLD)$(filter_a_la_LINK) -rpath $(pkglibdir) $(filter_a_la_OBJECTS) $(filter_a_la_LIBADD) $(LIBS) + +filter-aaaa.la: $(filter_aaaa_la_OBJECTS) $(filter_aaaa_la_DEPENDENCIES) $(EXTRA_filter_aaaa_la_DEPENDENCIES) + $(AM_V_CCLD)$(filter_aaaa_la_LINK) -rpath $(pkglibdir) $(filter_aaaa_la_OBJECTS) $(filter_aaaa_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter-a.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter-aaaa.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(pkglibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/filter-a.Plo + -rm -f ./$(DEPDIR)/filter-aaaa.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-pkglibLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/filter-a.Plo + -rm -f ./$(DEPDIR)/filter-aaaa.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-pkglibLTLIBRARIES + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-pkglibLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir \ + doc-am doc-local dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkglibLTLIBRARIES \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am test-am test-local uninstall uninstall-am \ + uninstall-pkglibLTLIBRARIES unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/plugins/filter-a.c b/bin/plugins/filter-a.c new file mode 100644 index 0000000..8556cb7 --- /dev/null +++ b/bin/plugins/filter-a.c @@ -0,0 +1,879 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/* aliases for the exported symbols */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +/* + * Possible values for the settings of filter-a-on-v6 and + * filter-a-on-v4: "no" is NONE, "yes" is FILTER, "break-dnssec" + * is BREAK_DNSSEC. + */ +typedef enum { NONE = 0, FILTER = 1, BREAK_DNSSEC = 2 } filter_a_t; + +/* + * Persistent data for use by this module. This will be associated + * with client object address in the hash table, and will remain + * accessible until the client object is detached. + */ +typedef struct filter_data { + filter_a_t mode; + uint32_t flags; +} filter_data_t; + +typedef struct filter_instance { + ns_plugin_t *module; + isc_mem_t *mctx; + + /* + * Hash table associating a client object with its persistent data. + */ + isc_ht_t *ht; + isc_mutex_t hlock; + + /* + * Values configured when the module is loaded. + */ + filter_a_t v4_a; + filter_a_t v6_a; + dns_acl_t *a_acl; +} filter_instance_t; + +/* + * Per-client flags set by this module + */ +#define FILTER_A_RECURSING 0x0001 /* Recursing for AAAA */ +#define FILTER_A_FILTERED 0x0002 /* A was removed from answer */ + +/* + * Client attribute tests. + */ +#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0) +#define RECURSIONOK(c) (((c)->query.attributes & NS_QUERYATTR_RECURSIONOK) != 0) + +/* + * Forward declarations of functions referenced in install_hooks(). + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); + +/*% + * Register the functions to be called at each hook point in 'hooktable', using + * memory context 'mctx' for allocating copies of stack-allocated structures + * passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata' + * argument to every callback. + */ +static void +install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx, + filter_instance_t *inst) { + const ns_hook_t filter_init = { + .action = filter_qctx_initialize, + .action_data = inst, + }; + + const ns_hook_t filter_respbegin = { + .action = filter_respond_begin, + .action_data = inst, + }; + + const ns_hook_t filter_respanyfound = { + .action = filter_respond_any_found, + .action_data = inst, + }; + + const ns_hook_t filter_prepresp = { + .action = filter_prep_response_begin, + .action_data = inst, + }; + + const ns_hook_t filter_donesend = { + .action = filter_query_done_send, + .action_data = inst, + }; + + const ns_hook_t filter_destroy = { + .action = filter_qctx_destroy, + .action_data = inst, + }; + + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &filter_init); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_BEGIN, &filter_respbegin); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_ANY_FOUND, + &filter_respanyfound); + ns_hook_add(hooktable, mctx, NS_QUERY_PREP_RESPONSE_BEGIN, + &filter_prepresp); + ns_hook_add(hooktable, mctx, NS_QUERY_DONE_SEND, &filter_donesend); + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &filter_destroy); +} + +/** +** Support for parsing of parameters and configuration of the module. +**/ + +/* + * Support for parsing of parameters. + */ +static const char *filter_a_enums[] = { "break-dnssec", NULL }; + +static isc_result_t +parse_filter_a(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} + +static void +doc_filter_a(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean); +} + +static cfg_type_t cfg_type_filter_a = { + "filter_a", parse_filter_a, cfg_print_ustring, + doc_filter_a, &cfg_rep_string, filter_a_enums, +}; + +static cfg_clausedef_t param_clauses[] = { + { "filter-a", &cfg_type_bracketed_aml, 0 }, + { "filter-a-on-v6", &cfg_type_filter_a, 0 }, + { "filter-a-on-v4", &cfg_type_filter_a, 0 }, +}; + +static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; + +static cfg_type_t cfg_type_parameters = { "filter-a-params", cfg_parse_mapbody, + cfg_print_mapbody, cfg_doc_mapbody, + &cfg_rep_map, param_clausesets }; + +static isc_result_t +parse_filter_a_on(const cfg_obj_t *param_obj, const char *param_name, + filter_a_t *dstp) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = cfg_map_get(param_obj, param_name, &obj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + *dstp = FILTER; + } else { + *dstp = NONE; + } + } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) { + *dstp = BREAK_DNSSEC; + } else { + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +static isc_result_t +check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *aclobj = NULL; + dns_acl_t *acl = NULL; + filter_a_t f4 = NONE, f6 = NONE; + + cfg_map_get(fmap, "filter-a", &aclobj); + if (aclobj == NULL) { + return (result); + } + + CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, &acl)); + + CHECK(parse_filter_a_on(fmap, "filter-a-on-v6", &f6)); + CHECK(parse_filter_a_on(fmap, "filter-a-on-v4", &f4)); + + if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-a\" is 'none;' but " + "either filter-a-on-v6 or filter-a-on-v4 " + "is enabled"); + result = ISC_R_FAILURE; + } else if (f4 == NONE && f6 == NONE && !dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-a\" is set but " + "neither filter-a-on-v6 or filter-a-on-v4 " + "is enabled"); + result = ISC_R_FAILURE; + } + +cleanup: + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +parse_parameters(filter_instance_t *inst, const char *parameters, + const void *cfg, const char *cfg_file, unsigned long cfg_line, + isc_mem_t *mctx, isc_log_t *lctx, void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + const cfg_obj_t *obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + + CHECK(parse_filter_a_on(param_obj, "filter-a-on-v6", &inst->v6_a)); + CHECK(parse_filter_a_on(param_obj, "filter-a-on-v4", &inst->v4_a)); + + result = cfg_map_get(param_obj, "filter-a", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, + &inst->a_acl)); + } else { + CHECK(dns_acl_any(mctx, &inst->a_acl)); + } + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/** +** Mandatory plugin API functions: +** +** - plugin_destroy +** - plugin_register +** - plugin_version +** - plugin_check +**/ + +/* + * Called by ns_plugin_register() to initialize the plugin and + * register hook functions into the view hook table. + */ +isc_result_t +plugin_register(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx, ns_hooktable_t *hooktable, void **instp) { + filter_instance_t *inst = NULL; + isc_result_t result = ISC_R_SUCCESS; + + isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, + ISC_LOG_INFO, + "registering 'filter-a' " + "module from %s:%lu, %s parameters", + cfg_file, cfg_line, parameters != NULL ? "with" : "no"); + + inst = isc_mem_get(mctx, sizeof(*inst)); + memset(inst, 0, sizeof(*inst)); + isc_mem_attach(mctx, &inst->mctx); + + if (parameters != NULL) { + CHECK(parse_parameters(inst, parameters, cfg, cfg_file, + cfg_line, mctx, lctx, actx)); + } + + isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + isc_mutex_init(&inst->hlock); + + /* + * Set hook points in the view's hooktable. + */ + install_hooks(hooktable, mctx, inst); + + *instp = inst; + +cleanup: + if (result != ISC_R_SUCCESS) { + plugin_destroy((void **)&inst); + } + + return (result); +} + +isc_result_t +plugin_check(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/* + * Called by ns_plugins_free(); frees memory allocated by + * the module when it was registered. + */ +void +plugin_destroy(void **instp) { + filter_instance_t *inst = (filter_instance_t *)*instp; + + if (inst->ht != NULL) { + isc_ht_destroy(&inst->ht); + isc_mutex_destroy(&inst->hlock); + } + if (inst->a_acl != NULL) { + dns_acl_detach(&inst->a_acl); + } + + isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst)); + *instp = NULL; + + return; +} + +/* + * Returns plugin API version for compatibility checks. + */ +int +plugin_version(void) { + return (NS_PLUGIN_VERSION); +} + +/** +** "filter-a" feature implementation begins here. +**/ + +/*% + * Structure describing the filtering to be applied by process_section(). + */ +typedef struct section_filter { + query_ctx_t *qctx; + filter_a_t mode; + dns_section_t section; + const dns_name_t *name; + dns_rdatatype_t type; + bool only_if_aaaa_exists; +} section_filter_t; + +/* + * Check whether this is an IPv4 client. + */ +static bool +is_v4_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) { + return (true); + } + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +/* + * Check whether this is an IPv6 client. + */ +static bool +is_v6_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +static filter_data_t * +client_state_get(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = NULL; + isc_result_t result; + + LOCK(&inst->hlock); + result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), (void **)&client_state); + UNLOCK(&inst->hlock); + + return (result == ISC_R_SUCCESS ? client_state : NULL); +} + +static void +client_state_create(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state; + isc_result_t result; + + client_state = isc_mem_get(inst->mctx, sizeof(*client_state)); + + client_state->mode = NONE; + client_state->flags = 0; + + LOCK(&inst->hlock); + result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), client_state); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +static void +client_state_destroy(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + if (client_state == NULL) { + return; + } + + LOCK(&inst->hlock); + result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client)); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_mem_put(inst->mctx, client_state, sizeof(*client_state)); +} + +/*% + * Mark 'rdataset' and 'sigrdataset' as rendered, gracefully handling NULL + * pointers and non-associated rdatasets. + */ +static void +mark_as_rendered(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { + if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } +} + +/*% + * Check whether an RRset of given 'type' is present at given 'name'. If + * it is found and either it is not signed or the combination of query + * flags and configured processing 'mode' allows it, mark the RRset and its + * associated signatures as already rendered to prevent them from appearing + * in the response message stored in 'qctx'. If 'only_if_aaaa_exists' is + * true, an RRset of type AAAA must also exist at 'name' in order for the + * above processing to happen. + */ +static bool +process_name(query_ctx_t *qctx, filter_a_t mode, const dns_name_t *name, + dns_rdatatype_t type, bool only_if_aaaa_exists) { + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + isc_result_t result; + bool modified = false; + + if (only_if_aaaa_exists) { + CHECK(dns_message_findtype(name, dns_rdatatype_aaaa, 0, NULL)); + } + + (void)dns_message_findtype(name, type, 0, &rdataset); + (void)dns_message_findtype(name, dns_rdatatype_rrsig, type, + &sigrdataset); + + if (rdataset != NULL && + (sigrdataset == NULL || !WANTDNSSEC(qctx->client) || + mode == BREAK_DNSSEC)) + { + /* + * An RRset of given 'type' was found at 'name' and at least + * one of the following is true: + * + * - the RRset is not signed, + * - the client did not set the DO bit in its request, + * - configuration allows us to tamper with signed responses. + * + * This means it is okay to filter out this RRset and its + * signatures, if any, from the response. + */ + mark_as_rendered(rdataset, sigrdataset); + modified = true; + } + +cleanup: + return (modified); +} + +/*% + * Apply the requested section filter, i.e. prevent (when possible, as + * determined by process_name()) RRsets of given 'type' from being rendered + * in the given 'section' of the response message stored in 'qctx'. Clear + * the AD bit if the answer and/or authority section was modified. If + * 'name' is NULL, all names in the given 'section' are processed; + * otherwise, only 'name' is. 'only_if_aaaa_exists' is passed through to + * process_name(). + */ +static void +process_section(const section_filter_t *filter) { + query_ctx_t *qctx = filter->qctx; + filter_a_t mode = filter->mode; + dns_section_t section = filter->section; + const dns_name_t *name = filter->name; + dns_rdatatype_t type = filter->type; + bool only_if_aaaa_exists = filter->only_if_aaaa_exists; + + dns_message_t *message = qctx->client->message; + isc_result_t result; + + for (result = dns_message_firstname(message, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, section)) + { + dns_name_t *cur = NULL; + dns_message_currentname(message, section, &cur); + if (name != NULL && !dns_name_equal(name, cur)) { + /* + * We only want to process 'name' and this is not it. + */ + continue; + } + + if (!process_name(qctx, mode, cur, type, only_if_aaaa_exists)) { + /* + * Response was not modified, do not touch the AD bit. + */ + continue; + } + + if (section == DNS_SECTION_ANSWER || + section == DNS_SECTION_AUTHORITY) + { + message->flags &= ~DNS_MESSAGEFLAG_AD; + } + } +} + +/* + * Initialize filter state, fetching it from a memory pool and storing it + * in a hash table keyed according to the client object; this enables us to + * retrieve persistent data related to a client query for as long as the + * object persists. + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state; + + *resp = ISC_R_UNSET; + + client_state = client_state_get(qctx, inst); + if (client_state == NULL) { + client_state_create(qctx, inst); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Determine whether this client should have A filtered or not, based on + * the client address family and the settings of filter-a-on-v6 and + * filter-a-on-v4. + */ +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (inst->v4_a != NONE || inst->v6_a != NONE) { + result = ns_client_checkaclsilent(qctx->client, NULL, + inst->a_acl, true); + if (result == ISC_R_SUCCESS && inst->v4_a != NONE && + is_v4_client(qctx->client)) + { + client_state->mode = inst->v4_a; + } else if (result == ISC_R_SUCCESS && inst->v6_a != NONE && + is_v6_client(qctx->client)) + { + client_state->mode = inst->v6_a; + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide A rrsets if there is a matching AAAA. Trigger recursion if + * necessary to find out whether an AAAA exists. + * + * (This version is for processing answers to explicit A queries; ANY + * queries are handled in filter_respond_any_found().) + */ +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result = ISC_R_UNSET; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (client_state->mode != BREAK_DNSSEC && + (client_state->mode != FILTER || + (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL && + dns_rdataset_isassociated(qctx->sigrdataset)))) + { + return (NS_HOOK_CONTINUE); + } + + if (qctx->qtype == dns_rdatatype_a) { + dns_rdataset_t *trdataset; + trdataset = ns_client_newrdataset(qctx->client); + result = dns_db_findrdataset( + qctx->db, qctx->node, qctx->version, dns_rdatatype_aaaa, + 0, qctx->client->now, trdataset, NULL); + if (dns_rdataset_isassociated(trdataset)) { + dns_rdataset_disassociate(trdataset); + } + ns_client_putrdataset(qctx->client, &trdataset); + + /* + * We found an A. If we also found an AAAA, then the A + * must not be rendered. + * + * If the AAAA is not in our cache, then any result other than + * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no AAAAA, + * and so AAAAs are okay. + * + * We assume there is no AAAA if we can't recurse for this + * client. That might be the wrong answer, but what else + * can we do? Besides, the fact that we have the A and + * are using this mechanism in the first place suggests + * that we care more about AAAAs than As, and would have + * cached an AAAA if it existed. + */ + if (result == ISC_R_SUCCESS) { + mark_as_rendered(qctx->rdataset, qctx->sigrdataset); + qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD; + client_state->flags |= FILTER_A_FILTERED; + } else if (!qctx->authoritative && RECURSIONOK(qctx->client) && + (result == DNS_R_DELEGATION || + result == ISC_R_NOTFOUND)) + { + /* + * This is an ugly kludge to recurse + * for the AAAA and discard the result.??? + * + * Continue to add the A now. + * We'll make a note to not render it + * if the recursion for the AAAA succeeds. + */ + result = ns_query_recurse(qctx->client, + dns_rdatatype_aaaa, + qctx->client->query.qname, + NULL, NULL, qctx->resuming); + if (result == ISC_R_SUCCESS) { + client_state->flags |= FILTER_A_RECURSING; + qctx->client->query.attributes |= + NS_QUERYATTR_RECURSING; + } + } + } else if (qctx->qtype == dns_rdatatype_aaaa && + (client_state->flags & FILTER_A_RECURSING) != 0) + { + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->fname, + .type = dns_rdatatype_a, + }; + process_section(&filter_answer); + + client_state->flags &= ~FILTER_A_RECURSING; + + result = ns_query_done(qctx); + + *resp = result; + + return (NS_HOOK_RETURN); + } + + *resp = result; + return (NS_HOOK_CONTINUE); +} + +/* + * When answering an ANY query, remove A if AAAA is present. + */ +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + /* + * If we are authoritative, require an AAAA record to be + * present before filtering out A records; otherwise, + * just assume an AAAA record exists even if it was not in the + * cache (and therefore is not in the response message), + * thus proceeding with filtering out A records. + */ + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->tname, + .type = dns_rdatatype_a, + .only_if_aaaa_exists = qctx->authoritative, + }; + process_section(&filter_answer); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide A rrsets in the additional section if there is a matching AAAA, and + * hide NS in the authority section if A was filtered in the answer + * section. + */ +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + const section_filter_t filter_additional = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ADDITIONAL, + .type = dns_rdatatype_a, + .only_if_aaaa_exists = true, + }; + process_section(&filter_additional); + + if ((client_state->flags & FILTER_A_FILTERED) != 0) { + const section_filter_t filter_authority = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_AUTHORITY, + .type = dns_rdatatype_ns, + }; + process_section(&filter_authority); + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * If the client is being detached, then we can delete our persistent data + * from hash table and return it to the memory pool. + */ +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + + *resp = ISC_R_UNSET; + + if (!qctx->detach_client) { + return (NS_HOOK_CONTINUE); + } + + client_state_destroy(qctx, inst); + + return (NS_HOOK_CONTINUE); +} diff --git a/bin/plugins/filter-a.rst b/bin/plugins/filter-a.rst new file mode 100644 index 0000000..16b3dee --- /dev/null +++ b/bin/plugins/filter-a.rst @@ -0,0 +1,86 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: filter-a +.. _man_filter-a: + +filter-a.so - filter A in DNS responses when AAAA is present +--------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`plugin query` "filter-a.so" [{ parameters }]; + +Description +~~~~~~~~~~~ + +:program:`filter-a.so` is a query plugin module for :iscman:`named`, enabling +:iscman:`named` to omit some IPv4 addresses when responding to clients. + +For example: + +:: + + plugin query "filter-a.so" { + filter-a-on-v6 yes; + filter-a-on-v4 yes; + filter-a { 192.0.2.1; 2001:db8:2::1; }; + }; + +This module is intended to aid transition from IPv4 to IPv6 by +withholding IPv4 addresses from DNS clients which are not connected to +the IPv4 Internet, when the name being looked up has an IPv6 address +available. Use of this module is not recommended unless absolutely +necessary. + +Note: This mechanism can erroneously cause other servers not to give +A records to their clients. If a recursing server with both IPv6 and +IPv4 network connections queries an authoritative server using this +mechanism via IPv6, it is denied A records even if its client is +using IPv4. + +Options +~~~~~~~ + +``filter-a`` + This option specifies a list of client addresses for which A filtering is to + be applied. The default is ``any``. + +``filter-a-on-v6`` + If set to ``yes``, this option indicates that the DNS client is at an IPv6 address, in + ``filter-a``. If the response does not include DNSSEC + signatures, then all A records are deleted from the response. This + filtering applies to all responses, not only authoritative + ones. + + If set to ``break-dnssec``, then A records are deleted even when + DNSSEC is enabled. As suggested by the name, this causes the response + to fail to verify, because the DNSSEC protocol is designed to detect + deletions. + + This mechanism can erroneously cause other servers not to give A + records to their clients. If a recursing server with both IPv6 and IPv4 + network connections queries an authoritative server using this + mechanism via IPv6, it is denied A records even if its client is + using IPv4. + +``filter-a-on-v4`` + This option is identical to ``filter-a-on-v6``, except that it filters A responses + to queries from IPv4 clients instead of IPv6 clients. To filter all + responses, set both options to ``yes``. + +See Also +~~~~~~~~ + +BIND 9 Administrator Reference Manual. diff --git a/bin/plugins/filter-aaaa.c b/bin/plugins/filter-aaaa.c new file mode 100644 index 0000000..1ec1fd8 --- /dev/null +++ b/bin/plugins/filter-aaaa.c @@ -0,0 +1,881 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/* aliases for the exported symbols */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +/* + * Possible values for the settings of filter-aaaa-on-v4 and + * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec" + * is BREAK_DNSSEC. + */ +typedef enum { NONE = 0, FILTER = 1, BREAK_DNSSEC = 2 } filter_aaaa_t; + +/* + * Persistent data for use by this module. This will be associated + * with client object address in the hash table, and will remain + * accessible until the client object is detached. + */ +typedef struct filter_data { + filter_aaaa_t mode; + uint32_t flags; +} filter_data_t; + +typedef struct filter_instance { + ns_plugin_t *module; + isc_mem_t *mctx; + + /* + * Hash table associating a client object with its persistent data. + */ + isc_ht_t *ht; + isc_mutex_t hlock; + + /* + * Values configured when the module is loaded. + */ + filter_aaaa_t v4_aaaa; + filter_aaaa_t v6_aaaa; + dns_acl_t *aaaa_acl; +} filter_instance_t; + +/* + * Per-client flags set by this module + */ +#define FILTER_AAAA_RECURSING 0x0001 /* Recursing for A */ +#define FILTER_AAAA_FILTERED 0x0002 /* AAAA was removed from answer */ + +/* + * Client attribute tests. + */ +#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0) +#define RECURSIONOK(c) (((c)->query.attributes & NS_QUERYATTR_RECURSIONOK) != 0) + +/* + * Forward declarations of functions referenced in install_hooks(). + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp); +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp); + +/*% + * Register the functions to be called at each hook point in 'hooktable', using + * memory context 'mctx' for allocating copies of stack-allocated structures + * passed to ns_hook_add(). Make sure 'inst' will be passed as the 'cbdata' + * argument to every callback. + */ +static void +install_hooks(ns_hooktable_t *hooktable, isc_mem_t *mctx, + filter_instance_t *inst) { + const ns_hook_t filter_init = { + .action = filter_qctx_initialize, + .action_data = inst, + }; + + const ns_hook_t filter_respbegin = { + .action = filter_respond_begin, + .action_data = inst, + }; + + const ns_hook_t filter_respanyfound = { + .action = filter_respond_any_found, + .action_data = inst, + }; + + const ns_hook_t filter_prepresp = { + .action = filter_prep_response_begin, + .action_data = inst, + }; + + const ns_hook_t filter_donesend = { + .action = filter_query_done_send, + .action_data = inst, + }; + + const ns_hook_t filter_destroy = { + .action = filter_qctx_destroy, + .action_data = inst, + }; + + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_INITIALIZED, &filter_init); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_BEGIN, &filter_respbegin); + ns_hook_add(hooktable, mctx, NS_QUERY_RESPOND_ANY_FOUND, + &filter_respanyfound); + ns_hook_add(hooktable, mctx, NS_QUERY_PREP_RESPONSE_BEGIN, + &filter_prepresp); + ns_hook_add(hooktable, mctx, NS_QUERY_DONE_SEND, &filter_donesend); + ns_hook_add(hooktable, mctx, NS_QUERY_QCTX_DESTROYED, &filter_destroy); +} + +/** +** Support for parsing of parameters and configuration of the module. +**/ + +/* + * Support for parsing of parameters. + */ +static const char *filter_aaaa_enums[] = { "break-dnssec", NULL }; + +static isc_result_t +parse_filter_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret)); +} + +static void +doc_filter_aaaa(cfg_printer_t *pctx, const cfg_type_t *type) { + cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean); +} + +static cfg_type_t cfg_type_filter_aaaa = { + "filter_aaaa", parse_filter_aaaa, cfg_print_ustring, + doc_filter_aaaa, &cfg_rep_string, filter_aaaa_enums, +}; + +static cfg_clausedef_t param_clauses[] = { + { "filter-aaaa", &cfg_type_bracketed_aml, 0 }, + { "filter-aaaa-on-v4", &cfg_type_filter_aaaa, 0 }, + { "filter-aaaa-on-v6", &cfg_type_filter_aaaa, 0 }, +}; + +static cfg_clausedef_t *param_clausesets[] = { param_clauses, NULL }; + +static cfg_type_t cfg_type_parameters = { + "filter-aaaa-params", cfg_parse_mapbody, cfg_print_mapbody, + cfg_doc_mapbody, &cfg_rep_map, param_clausesets +}; + +static isc_result_t +parse_filter_aaaa_on(const cfg_obj_t *param_obj, const char *param_name, + filter_aaaa_t *dstp) { + const cfg_obj_t *obj = NULL; + isc_result_t result; + + result = cfg_map_get(param_obj, param_name, &obj); + if (result != ISC_R_SUCCESS) { + return (ISC_R_SUCCESS); + } + + if (cfg_obj_isboolean(obj)) { + if (cfg_obj_asboolean(obj)) { + *dstp = FILTER; + } else { + *dstp = NONE; + } + } else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) { + *dstp = BREAK_DNSSEC; + } else { + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +static isc_result_t +check_syntax(cfg_obj_t *fmap, const void *cfg, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *aclobj = NULL; + dns_acl_t *acl = NULL; + filter_aaaa_t f4 = NONE, f6 = NONE; + + cfg_map_get(fmap, "filter-aaaa", &aclobj); + if (aclobj == NULL) { + return (result); + } + + CHECK(cfg_acl_fromconfig(aclobj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, &acl)); + + CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v4", &f4)); + CHECK(parse_filter_aaaa_on(fmap, "filter-aaaa-on-v6", &f6)); + + if ((f4 != NONE || f6 != NONE) && dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-aaaa\" is 'none;' but " + "either filter-aaaa-on-v4 or filter-aaaa-on-v6 " + "is enabled"); + result = ISC_R_FAILURE; + } else if (f4 == NONE && f6 == NONE && !dns_acl_isnone(acl)) { + cfg_obj_log(aclobj, lctx, ISC_LOG_WARNING, + "\"filter-aaaa\" is set but " + "neither filter-aaaa-on-v4 or filter-aaaa-on-v6 " + "is enabled"); + result = ISC_R_FAILURE; + } + +cleanup: + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +parse_parameters(filter_instance_t *inst, const char *parameters, + const void *cfg, const char *cfg_file, unsigned long cfg_line, + isc_mem_t *mctx, isc_log_t *lctx, void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + const cfg_obj_t *obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + + CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v4", + &inst->v4_aaaa)); + CHECK(parse_filter_aaaa_on(param_obj, "filter-aaaa-on-v6", + &inst->v6_aaaa)); + + result = cfg_map_get(param_obj, "filter-aaaa", &obj); + if (result == ISC_R_SUCCESS) { + CHECK(cfg_acl_fromconfig(obj, (const cfg_obj_t *)cfg, lctx, + (cfg_aclconfctx_t *)actx, mctx, 0, + &inst->aaaa_acl)); + } else { + CHECK(dns_acl_any(mctx, &inst->aaaa_acl)); + } + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/** +** Mandatory plugin API functions: +** +** - plugin_destroy +** - plugin_register +** - plugin_version +** - plugin_check +**/ + +/* + * Called by ns_plugin_register() to initialize the plugin and + * register hook functions into the view hook table. + */ +isc_result_t +plugin_register(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx, ns_hooktable_t *hooktable, void **instp) { + filter_instance_t *inst = NULL; + isc_result_t result = ISC_R_SUCCESS; + + isc_log_write(lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, + ISC_LOG_INFO, + "registering 'filter-aaaa' " + "module from %s:%lu, %s parameters", + cfg_file, cfg_line, parameters != NULL ? "with" : "no"); + + inst = isc_mem_get(mctx, sizeof(*inst)); + memset(inst, 0, sizeof(*inst)); + isc_mem_attach(mctx, &inst->mctx); + + if (parameters != NULL) { + CHECK(parse_parameters(inst, parameters, cfg, cfg_file, + cfg_line, mctx, lctx, actx)); + } + + isc_ht_init(&inst->ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + isc_mutex_init(&inst->hlock); + + /* + * Set hook points in the view's hooktable. + */ + install_hooks(hooktable, mctx, inst); + + *instp = inst; + +cleanup: + if (result != ISC_R_SUCCESS) { + plugin_destroy((void **)&inst); + } + + return (result); +} + +isc_result_t +plugin_check(const char *parameters, const void *cfg, const char *cfg_file, + unsigned long cfg_line, isc_mem_t *mctx, isc_log_t *lctx, + void *actx) { + isc_result_t result = ISC_R_SUCCESS; + cfg_parser_t *parser = NULL; + cfg_obj_t *param_obj = NULL; + isc_buffer_t b; + + CHECK(cfg_parser_create(mctx, lctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + CHECK(cfg_parse_buffer(parser, &b, cfg_file, cfg_line, + &cfg_type_parameters, 0, ¶m_obj)); + + CHECK(check_syntax(param_obj, cfg, mctx, lctx, actx)); + +cleanup: + if (param_obj != NULL) { + cfg_obj_destroy(parser, ¶m_obj); + } + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + return (result); +} + +/* + * Called by ns_plugins_free(); frees memory allocated by + * the module when it was registered. + */ +void +plugin_destroy(void **instp) { + filter_instance_t *inst = (filter_instance_t *)*instp; + + if (inst->ht != NULL) { + isc_ht_destroy(&inst->ht); + isc_mutex_destroy(&inst->hlock); + } + if (inst->aaaa_acl != NULL) { + dns_acl_detach(&inst->aaaa_acl); + } + + isc_mem_putanddetach(&inst->mctx, inst, sizeof(*inst)); + *instp = NULL; + + return; +} + +/* + * Returns plugin API version for compatibility checks. + */ +int +plugin_version(void) { + return (NS_PLUGIN_VERSION); +} + +/** +** "filter-aaaa" feature implementation begins here. +**/ + +/*% + * Structure describing the filtering to be applied by process_section(). + */ +typedef struct section_filter { + query_ctx_t *qctx; + filter_aaaa_t mode; + dns_section_t section; + const dns_name_t *name; + dns_rdatatype_t type; + bool only_if_a_exists; +} section_filter_t; + +/* + * Check whether this is an IPv4 client. + */ +static bool +is_v4_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) { + return (true); + } + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +/* + * Check whether this is an IPv6 client. + */ +static bool +is_v6_client(ns_client_t *client) { + if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 && + !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr)) + { + return (true); + } + return (false); +} + +static filter_data_t * +client_state_get(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = NULL; + isc_result_t result; + + LOCK(&inst->hlock); + result = isc_ht_find(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), (void **)&client_state); + UNLOCK(&inst->hlock); + + return (result == ISC_R_SUCCESS ? client_state : NULL); +} + +static void +client_state_create(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state; + isc_result_t result; + + client_state = isc_mem_get(inst->mctx, sizeof(*client_state)); + + client_state->mode = NONE; + client_state->flags = 0; + + LOCK(&inst->hlock); + result = isc_ht_add(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client), client_state); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); +} + +static void +client_state_destroy(const query_ctx_t *qctx, filter_instance_t *inst) { + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + if (client_state == NULL) { + return; + } + + LOCK(&inst->hlock); + result = isc_ht_delete(inst->ht, (const unsigned char *)&qctx->client, + sizeof(qctx->client)); + UNLOCK(&inst->hlock); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + isc_mem_put(inst->mctx, client_state, sizeof(*client_state)); +} + +/*% + * Mark 'rdataset' and 'sigrdataset' as rendered, gracefully handling NULL + * pointers and non-associated rdatasets. + */ +static void +mark_as_rendered(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) { + if (rdataset != NULL && dns_rdataset_isassociated(rdataset)) { + rdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } + if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) { + sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED; + } +} + +/*% + * Check whether an RRset of given 'type' is present at given 'name'. If + * it is found and either it is not signed or the combination of query + * flags and configured processing 'mode' allows it, mark the RRset and its + * associated signatures as already rendered to prevent them from appearing + * in the response message stored in 'qctx'. If 'only_if_a_exists' is + * true, an RRset of type A must also exist at 'name' in order for the + * above processing to happen. + */ +static bool +process_name(query_ctx_t *qctx, filter_aaaa_t mode, const dns_name_t *name, + dns_rdatatype_t type, bool only_if_a_exists) { + dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; + isc_result_t result; + bool modified = false; + + if (only_if_a_exists) { + CHECK(dns_message_findtype(name, dns_rdatatype_a, 0, NULL)); + } + + (void)dns_message_findtype(name, type, 0, &rdataset); + (void)dns_message_findtype(name, dns_rdatatype_rrsig, type, + &sigrdataset); + + if (rdataset != NULL && + (sigrdataset == NULL || !WANTDNSSEC(qctx->client) || + mode == BREAK_DNSSEC)) + { + /* + * An RRset of given 'type' was found at 'name' and at least + * one of the following is true: + * + * - the RRset is not signed, + * - the client did not set the DO bit in its request, + * - configuration allows us to tamper with signed responses. + * + * This means it is okay to filter out this RRset and its + * signatures, if any, from the response. + */ + mark_as_rendered(rdataset, sigrdataset); + modified = true; + } + +cleanup: + return (modified); +} + +/*% + * Apply the requested section filter, i.e. prevent (when possible, as + * determined by process_name()) RRsets of given 'type' from being rendered + * in the given 'section' of the response message stored in 'qctx'. Clear + * the AD bit if the answer and/or authority section was modified. If + * 'name' is NULL, all names in the given 'section' are processed; + * otherwise, only 'name' is. 'only_if_a_exists' is passed through to + * process_name(). + */ +static void +process_section(const section_filter_t *filter) { + query_ctx_t *qctx = filter->qctx; + filter_aaaa_t mode = filter->mode; + dns_section_t section = filter->section; + const dns_name_t *name = filter->name; + dns_rdatatype_t type = filter->type; + bool only_if_a_exists = filter->only_if_a_exists; + + dns_message_t *message = qctx->client->message; + isc_result_t result; + + for (result = dns_message_firstname(message, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(message, section)) + { + dns_name_t *cur = NULL; + dns_message_currentname(message, section, &cur); + if (name != NULL && !dns_name_equal(name, cur)) { + /* + * We only want to process 'name' and this is not it. + */ + continue; + } + + if (!process_name(qctx, mode, cur, type, only_if_a_exists)) { + /* + * Response was not modified, do not touch the AD bit. + */ + continue; + } + + if (section == DNS_SECTION_ANSWER || + section == DNS_SECTION_AUTHORITY) + { + message->flags &= ~DNS_MESSAGEFLAG_AD; + } + } +} + +/* + * Initialize filter state, fetching it from a memory pool and storing it + * in a hash table keyed according to the client object; this enables us to + * retrieve persistent data related to a client query for as long as the + * object persists. + */ +static ns_hookresult_t +filter_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state; + + *resp = ISC_R_UNSET; + + client_state = client_state_get(qctx, inst); + if (client_state == NULL) { + client_state_create(qctx, inst); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Determine whether this client should have AAAA filtered or not, based on + * the client address family and the settings of filter-aaaa-on-v4 and + * filter-aaaa-on-v6. + */ +static ns_hookresult_t +filter_prep_response_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (inst->v4_aaaa != NONE || inst->v6_aaaa != NONE) { + result = ns_client_checkaclsilent(qctx->client, NULL, + inst->aaaa_acl, true); + if (result == ISC_R_SUCCESS && inst->v4_aaaa != NONE && + is_v4_client(qctx->client)) + { + client_state->mode = inst->v4_aaaa; + } else if (result == ISC_R_SUCCESS && inst->v6_aaaa != NONE && + is_v6_client(qctx->client)) + { + client_state->mode = inst->v6_aaaa; + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide AAAA rrsets if there is a matching A. Trigger recursion if + * necessary to find out whether an A exists. + * + * (This version is for processing answers to explicit AAAA queries; ANY + * queries are handled in filter_respond_any_found().) + */ +static ns_hookresult_t +filter_respond_begin(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + isc_result_t result = ISC_R_UNSET; + + *resp = ISC_R_UNSET; + + if (client_state == NULL) { + return (NS_HOOK_CONTINUE); + } + + if (client_state->mode != BREAK_DNSSEC && + (client_state->mode != FILTER || + (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL && + dns_rdataset_isassociated(qctx->sigrdataset)))) + { + return (NS_HOOK_CONTINUE); + } + + if (qctx->qtype == dns_rdatatype_aaaa) { + dns_rdataset_t *trdataset; + trdataset = ns_client_newrdataset(qctx->client); + result = dns_db_findrdataset( + qctx->db, qctx->node, qctx->version, dns_rdatatype_a, 0, + qctx->client->now, trdataset, NULL); + if (dns_rdataset_isassociated(trdataset)) { + dns_rdataset_disassociate(trdataset); + } + ns_client_putrdataset(qctx->client, &trdataset); + + /* + * We found an AAAA. If we also found an A, then the AAAA + * must not be rendered. + * + * If the A is not in our cache, then any result other than + * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A, + * and so AAAAs are okay. + * + * We assume there is no A if we can't recurse for this + * client. That might be the wrong answer, but what else + * can we do? Besides, the fact that we have the AAAA and + * are using this mechanism in the first place suggests + * that we care more about As than AAAAs, and would have + * cached an A if it existed. + */ + if (result == ISC_R_SUCCESS) { + mark_as_rendered(qctx->rdataset, qctx->sigrdataset); + qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AD; + client_state->flags |= FILTER_AAAA_FILTERED; + } else if (!qctx->authoritative && RECURSIONOK(qctx->client) && + (result == DNS_R_DELEGATION || + result == ISC_R_NOTFOUND)) + { + /* + * This is an ugly kludge to recurse + * for the A and discard the result. + * + * Continue to add the AAAA now. + * We'll make a note to not render it + * if the recursion for the A succeeds. + */ + result = ns_query_recurse(qctx->client, dns_rdatatype_a, + qctx->client->query.qname, + NULL, NULL, qctx->resuming); + if (result == ISC_R_SUCCESS) { + client_state->flags |= FILTER_AAAA_RECURSING; + qctx->client->query.attributes |= + NS_QUERYATTR_RECURSING; + } + } + } else if (qctx->qtype == dns_rdatatype_a && + (client_state->flags & FILTER_AAAA_RECURSING) != 0) + { + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->fname, + .type = dns_rdatatype_aaaa, + }; + process_section(&filter_answer); + + client_state->flags &= ~FILTER_AAAA_RECURSING; + + result = ns_query_done(qctx); + + *resp = result; + + return (NS_HOOK_RETURN); + } + + *resp = result; + return (NS_HOOK_CONTINUE); +} + +/* + * When answering an ANY query, remove AAAA if A is present. + */ +static ns_hookresult_t +filter_respond_any_found(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + /* + * If we are authoritative, require an A record to be + * present before filtering out AAAA records; otherwise, + * just assume an A record exists even if it was not in the + * cache (and therefore is not in the response message), + * thus proceeding with filtering out AAAA records. + */ + const section_filter_t filter_answer = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ANSWER, + .name = qctx->tname, + .type = dns_rdatatype_aaaa, + .only_if_a_exists = qctx->authoritative, + }; + process_section(&filter_answer); + } + + return (NS_HOOK_CONTINUE); +} + +/* + * Hide AAAA rrsets in the additional section if there is a matching A, and + * hide NS in the authority section if AAAA was filtered in the answer + * section. + */ +static ns_hookresult_t +filter_query_done_send(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + filter_data_t *client_state = client_state_get(qctx, inst); + + *resp = ISC_R_UNSET; + + if (client_state != NULL && client_state->mode != NONE) { + const section_filter_t filter_additional = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_ADDITIONAL, + .type = dns_rdatatype_aaaa, + .only_if_a_exists = true, + }; + process_section(&filter_additional); + + if ((client_state->flags & FILTER_AAAA_FILTERED) != 0) { + const section_filter_t filter_authority = { + .qctx = qctx, + .mode = client_state->mode, + .section = DNS_SECTION_AUTHORITY, + .type = dns_rdatatype_ns, + }; + process_section(&filter_authority); + } + } + + return (NS_HOOK_CONTINUE); +} + +/* + * If the client is being detached, then we can delete our persistent data + * from hash table and return it to the memory pool. + */ +static ns_hookresult_t +filter_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + filter_instance_t *inst = (filter_instance_t *)cbdata; + + *resp = ISC_R_UNSET; + + if (!qctx->detach_client) { + return (NS_HOOK_CONTINUE); + } + + client_state_destroy(qctx, inst); + + return (NS_HOOK_CONTINUE); +} diff --git a/bin/plugins/filter-aaaa.rst b/bin/plugins/filter-aaaa.rst new file mode 100644 index 0000000..8cd7556 --- /dev/null +++ b/bin/plugins/filter-aaaa.rst @@ -0,0 +1,90 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: filter-aaaa +.. _man_filter-aaaa: + +filter-aaaa.so - filter AAAA in DNS responses when A is present +--------------------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`plugin query` "filter-aaaa.so" [{ parameters }]; + +Description +~~~~~~~~~~~ + +:program:`filter-aaaa.so` is a query plugin module for :iscman:`named`, enabling +:iscman:`named` to omit some IPv6 addresses when responding to clients. + +Until BIND 9.12, this feature was implemented natively in :iscman:`named` and +enabled with the ``filter-aaaa`` ACL and the ``filter-aaaa-on-v4`` and +``filter-aaaa-on-v6`` options. These options are now deprecated in +:iscman:`named.conf` but can be passed as parameters to the +``filter-aaaa.so`` plugin, for example: + +:: + + plugin query "filter-aaaa.so" { + filter-aaaa-on-v4 yes; + filter-aaaa-on-v6 yes; + filter-aaaa { 192.0.2.1; 2001:db8:2::1; }; + }; + +This module is intended to aid transition from IPv4 to IPv6 by +withholding IPv6 addresses from DNS clients which are not connected to +the IPv6 Internet, when the name being looked up has an IPv4 address +available. Use of this module is not recommended unless absolutely +necessary. + +Note: This mechanism can erroneously cause other servers not to give +AAAA records to their clients. If a recursing server with both IPv6 and +IPv4 network connections queries an authoritative server using this +mechanism via IPv4, it is denied AAAA records even if its client is +using IPv6. + +Options +~~~~~~~ + +``filter-aaaa`` + This option specifies a list of client addresses for which AAAA filtering is to + be applied. The default is ``any``. + +``filter-aaaa-on-v4`` + If set to ``yes``, this option indicates that the DNS client is at an IPv4 address, in + ``filter-aaaa``. If the response does not include DNSSEC + signatures, then all AAAA records are deleted from the response. This + filtering applies to all responses, not only authoritative + ones. + + If set to ``break-dnssec``, then AAAA records are deleted even when + DNSSEC is enabled. As suggested by the name, this causes the response + to fail to verify, because the DNSSEC protocol is designed to detect + deletions. + + This mechanism can erroneously cause other servers not to give AAAA + records to their clients. If a recursing server with both IPv6 and IPv4 + network connections queries an authoritative server using this + mechanism via IPv4, it is denied AAAA records even if its client is + using IPv6. + +``filter-aaaa-on-v6`` + This option is identical to ``filter-aaaa-on-v4``, except that it filters AAAA responses + to queries from IPv6 clients instead of IPv4 clients. To filter all + responses, set both options to ``yes``. + +See Also +~~~~~~~~ + +BIND 9 Administrator Reference Manual. diff --git a/bin/rndc/Makefile.am b/bin/rndc/Makefile.am new file mode 100644 index 0000000..a668522 --- /dev/null +++ b/bin/rndc/Makefile.am @@ -0,0 +1,26 @@ +include $(top_srcdir)/Makefile.top + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) \ + $(LIBISCCC_CFLAGS) \ + $(LIBBIND9_CFLAGS) + +AM_CPPFLAGS += \ + -DRNDC_CONFFILE=\"${sysconfdir}/rndc.conf\" \ + -DRNDC_KEYFILE=\"${sysconfdir}/rndc.key\" + +sbin_PROGRAMS = rndc + +rndc_SOURCES = \ + rndc.c \ + util.c \ + util.h + +rndc_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCC_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) diff --git a/bin/rndc/Makefile.in b/bin/rndc/Makefile.in new file mode 100644 index 0000000..7fdcb83 --- /dev/null +++ b/bin/rndc/Makefile.in @@ -0,0 +1,831 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +sbin_PROGRAMS = rndc$(EXEEXT) +subdir = bin/rndc +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +PROGRAMS = $(sbin_PROGRAMS) +am_rndc_OBJECTS = rndc.$(OBJEXT) util.$(OBJEXT) +rndc_OBJECTS = $(am_rndc_OBJECTS) +rndc_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) $(LIBISCCC_LIBS) \ + $(LIBISCCFG_LIBS) $(LIBBIND9_LIBS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/rndc.Po ./$(DEPDIR)/util.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(rndc_SOURCES) +DIST_SOURCES = $(rndc_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) $(LIBDNS_CFLAGS) \ + $(LIBISCCFG_CFLAGS) $(LIBISCCC_CFLAGS) $(LIBBIND9_CFLAGS) \ + -DRNDC_CONFFILE=\"${sysconfdir}/rndc.conf\" \ + -DRNDC_KEYFILE=\"${sysconfdir}/rndc.key\" +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +rndc_SOURCES = \ + rndc.c \ + util.c \ + util.h + +rndc_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) \ + $(LIBISCCC_LIBS) \ + $(LIBISCCFG_LIBS) \ + $(LIBBIND9_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/rndc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/rndc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +rndc$(EXEEXT): $(rndc_OBJECTS) $(rndc_DEPENDENCIES) $(EXTRA_rndc_DEPENDENCIES) + @rm -f rndc$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rndc_OBJECTS) $(rndc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rndc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/rndc.Po + -rm -f ./$(DEPDIR)/util.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-am + +doc-am: doc-local + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/rndc.Po + -rm -f ./$(DEPDIR)/util.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +test: test-am + +test-am: test-local + +uninstall-am: uninstall-sbinPROGRAMS + +unit: unit-am + +unit-am: unit-local + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir doc-am doc-local dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am test-am test-local uninstall \ + uninstall-am uninstall-sbinPROGRAMS unit-am unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c new file mode 100644 index 0000000..87c36ba --- /dev/null +++ b/bin/rndc/rndc.c @@ -0,0 +1,1125 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "util.h" + +#define SERVERADDRS 10 +#define RNDC_TIMEOUT 60 * 1000 + +const char *progname = NULL; +bool verbose; + +static isc_nm_t *netmgr = NULL; +static isc_taskmgr_t *taskmgr = NULL; +static isc_task_t *rndc_task = NULL; + +static const char *admin_conffile = NULL; +static const char *admin_keyfile = NULL; +static const char *version = PACKAGE_VERSION; +static const char *servername = NULL; +static isc_sockaddr_t serveraddrs[SERVERADDRS]; +static isc_sockaddr_t local4, local6; +static bool local4set = false, local6set = false; +static int nserveraddrs; +static int currentaddr = 0; +static unsigned int remoteport = 0; +static isc_buffer_t *databuf = NULL; +static isccc_ccmsg_t rndc_ccmsg; +static uint32_t algorithm; +static isccc_region_t secret; +static bool failed = false; +static bool c_flag = false; +static isc_mem_t *rndc_mctx = NULL; +static atomic_uint_fast32_t sends = 0; +static atomic_uint_fast32_t recvs = 0; +static atomic_uint_fast32_t connects = 0; +static char *command = NULL; +static char *args = NULL; +static char program[256]; +static uint32_t serial; +static bool quiet = false; +static bool showresult = false; +static bool shuttingdown = false; +static isc_nmhandle_t *recvdone_handle = NULL; +static isc_nmhandle_t *recvnonce_handle = NULL; + +static void +rndc_startconnect(isc_sockaddr_t *addr); + +noreturn static void +usage(int status); + +static void +usage(int status) { + fprintf(stderr, "\ +Usage: %s [-b address] [-c config] [-s server] [-p port]\n\ + [-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\ +\n\ +command is one of the following:\n\ +\n\ + addzone zone [class [view]] { zone-options }\n\ + Add zone to given view. Requires allow-new-zones option.\n\ + delzone [-clean] zone [class [view]]\n\ + Removes zone from given view.\n\ + dnssec -checkds [-key id [-alg algorithm]] [-when time] (published|withdrawn) zone [class [view]]\n\ + Mark the DS record for the KSK of the given zone as seen\n\ + in the parent. If the zone has multiple KSKs, select a\n\ + specific key by providing the keytag with -key id and\n\ + optionally the key's algorithm with -alg algorithm.\n\ + Requires the zone to have a dnssec-policy.\n\ + dnssec -rollover -key id [-alg algorithm] [-when time] zone [class [view]]\n\ + Rollover key with id of the given zone. Requires the zone\n\ + to have a dnssec-policy.\n\ + dnssec -status zone [class [view]]\n\ + Show the DNSSEC signing state for the specified zone.\n\ + Requires the zone to have a dnssec-policy.\n\ + dnstap -reopen\n\ + Close, truncate and re-open the DNSTAP output file.\n\ + dnstap -roll [count]\n\ + Close, rename and re-open the DNSTAP output file(s).\n\ + dumpdb [-all|-cache|-zones|-adb|-bad|-expired|-fail] [view ...]\n\ + Dump cache(s) to the dump file (named_dump.db).\n\ + flush Flushes all of the server's caches.\n\ + flush [view] Flushes the server's cache for a view.\n\ + flushname name [view]\n\ + Flush the given name from the server's cache(s)\n\ + flushtree name [view]\n\ + Flush all names under the given name from the server's cache(s)\n\ + freeze Suspend updates to all dynamic zones.\n\ + freeze zone [class [view]]\n\ + Suspend updates to a dynamic zone.\n\ + halt Stop the server without saving pending updates.\n\ + halt -p Stop the server without saving pending updates reporting\n\ + process id.\n\ + loadkeys zone [class [view]]\n\ + Update keys without signing immediately.\n\ + managed-keys refresh [class [view]]\n\ + Check trust anchor for RFC 5011 key changes\n\ + managed-keys status [class [view]]\n\ + Display RFC 5011 managed keys information\n\ + managed-keys sync [class [view]]\n\ + Write RFC 5011 managed keys to disk\n\ + modzone zone [class [view]] { zone-options }\n\ + Modify a zone's configuration.\n\ + Requires allow-new-zones option.\n\ + notify zone [class [view]]\n\ + Resend NOTIFY messages for the zone.\n\ + notrace Set debugging level to 0.\n\ + nta -dump\n\ + List all negative trust anchors.\n\ + nta [-lifetime duration] [-force] domain [view]\n\ + Set a negative trust anchor, disabling DNSSEC validation\n\ + for the given domain.\n\ + Using -lifetime specifies the duration of the NTA, up\n\ + to one week.\n\ + Using -force prevents the NTA from expiring before its\n\ + full lifetime, even if the domain can validate sooner.\n\ + nta -remove domain [view]\n\ + Remove a negative trust anchor, re-enabling validation\n\ + for the given domain.\n\ + querylog [ on | off ]\n\ + Enable / disable query logging.\n\ + reconfig Reload configuration file and new zones only.\n\ + recursing Dump the queries that are currently recursing (named.recursing)\n\ + refresh zone [class [view]]\n\ + Schedule immediate maintenance for a zone.\n\ + reload Reload configuration file and zones.\n\ + reload zone [class [view]]\n\ + Reload a single zone.\n\ + retransfer zone [class [view]]\n\ + Retransfer a single zone without checking serial number.\n\ + scan Scan available network interfaces for changes.\n\ + secroots [view ...]\n\ + Write security roots to the secroots file.\n\ + serve-stale [ on | off | reset | status ] [class [view]]\n\ + Control whether stale answers are returned\n\ + showzone zone [class [view]]\n\ + Print a zone's configuration.\n\ + sign zone [class [view]]\n\ + Update zone keys, and sign as needed.\n\ + signing -clear all zone [class [view]]\n\ + Remove the private records for all keys that have\n\ + finished signing the given zone.\n\ + signing -clear / zone [class [view]]\n\ + Remove the private record that indicating the given key\n\ + has finished signing the given zone.\n\ + signing -list zone [class [view]]\n\ + List the private records showing the state of DNSSEC\n\ + signing in the given zone.\n\ + signing -nsec3param hash flags iterations salt zone [class [view]]\n\ + Add NSEC3 chain to zone if already signed.\n\ + Prime zone with NSEC3 chain if not yet signed.\n\ + signing -nsec3param none zone [class [view]]\n\ + Remove NSEC3 chains from zone.\n\ + signing -serial zone [class [view]]\n\ + Set the zones's serial to .\n\ + stats Write server statistics to the statistics file.\n\ + status Display status of the server.\n\ + stop Save pending updates to master files and stop the server.\n\ + stop -p Save pending updates to master files and stop the server\n\ + reporting process id.\n\ + sync [-clean] Dump changes to all dynamic zones to disk, and optionally\n\ + remove their journal files.\n\ + sync [-clean] zone [class [view]]\n\ + Dump a single zone's changes to disk, and optionally\n\ + remove its journal file.\n\ + tcp-timeouts Display the tcp-*-timeout option values\n\ + tcp-timeouts initial idle keepalive advertised\n\ + Update the tcp-*-timeout option values\n\ + thaw Enable updates to all dynamic zones and reload them.\n\ + thaw zone [class [view]]\n\ + Enable updates to a frozen dynamic zone and reload it.\n\ + trace Increment debugging level by one.\n\ + trace level Change the debugging level.\n\ + tsig-delete keyname [view]\n\ + Delete a TKEY-negotiated TSIG key.\n\ + tsig-list List all currently active TSIG keys, including both statically\n\ + configured and TKEY-negotiated keys.\n\ + validation [ on | off | status ] [view]\n\ + Enable / disable DNSSEC validation.\n\ + zonestatus zone [class [view]]\n\ + Display the current status of a zone.\n\ +\n\ +Version: %s\n", + progname, version); + + exit(status); +} + +#define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:Vy:" + +static void +preparse_args(int argc, char **argv) { + bool ipv4only = false, ipv6only = false; + int ch; + + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + default: + break; + } + } + + isc_commandline_reset = true; + isc_commandline_index = 1; +} + +static void +get_addresses(const char *host, in_port_t port) { + isc_result_t result; + int found = 0, count; + + REQUIRE(host != NULL); + + if (*host == '/') { + result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs], + host); + if (result == ISC_R_SUCCESS) { + nserveraddrs++; + } + } else { + count = SERVERADDRS - nserveraddrs; + result = bind9_getaddresses( + host, port, &serveraddrs[nserveraddrs], count, &found); + nserveraddrs += found; + } + if (result != ISC_R_SUCCESS) { + fatal("couldn't get address for '%s': %s", host, + isc_result_totext(result)); + } + INSIST(nserveraddrs > 0); +} + +static void +rndc_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + isc_nmhandle_t *sendhandle = (isc_nmhandle_t *)arg; + + if (result != ISC_R_SUCCESS) { + fatal("send failed: %s", isc_result_totext(result)); + } + + REQUIRE(sendhandle == handle); + isc_nmhandle_detach(&sendhandle); + + if (atomic_fetch_sub_release(&sends, 1) == 1 && + atomic_load_acquire(&recvs) == 0) + { + shuttingdown = true; + isc_task_detach(&rndc_task); + isc_app_shutdown(); + } +} + +static void +rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg; + isccc_sexpr_t *response = NULL; + isccc_sexpr_t *data = NULL; + isccc_region_t source; + char *errormsg = NULL; + char *textmsg = NULL; + + REQUIRE(ccmsg != NULL); + + if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) { + atomic_fetch_sub_release(&recvs, 1); + if (handle != NULL) { + REQUIRE(recvdone_handle == handle); + isc_nmhandle_detach(&recvdone_handle); + } + return; + } else if (result == ISC_R_EOF) { + fatal("connection to remote host closed.\n" + "* This may indicate that the\n" + "* remote server is using an older\n" + "* version of the command protocol,\n" + "* this host is not authorized to connect,\n" + "* the clocks are not synchronized,\n" + "* the key signing algorithm is incorrect,\n" + "* or the key is invalid."); + } else if (result != ISC_R_SUCCESS) { + fatal("recv failed: %s", isc_result_totext(result)); + } + + source.rstart = isc_buffer_base(ccmsg->buffer); + source.rend = isc_buffer_used(ccmsg->buffer); + + DO("parse message", + isccc_cc_fromwire(&source, &response, algorithm, &secret)); + + data = isccc_alist_lookup(response, "_data"); + if (!isccc_alist_alistp(data)) { + fatal("bad or missing data section in response"); + } + result = isccc_cc_lookupstring(data, "err", &errormsg); + if (result == ISC_R_SUCCESS) { + failed = true; + fprintf(stderr, "%s: '%s' failed: %s\n", progname, command, + errormsg); + } else if (result != ISC_R_NOTFOUND) { + fprintf(stderr, "%s: parsing response failed: %s\n", progname, + isc_result_totext(result)); + } + + result = isccc_cc_lookupstring(data, "text", &textmsg); + if (result == ISC_R_SUCCESS) { + if ((!quiet || failed) && strlen(textmsg) != 0U) { + fprintf(failed ? stderr : stdout, "%s\n", textmsg); + } + } else if (result != ISC_R_NOTFOUND) { + fprintf(stderr, "%s: parsing response failed: %s\n", progname, + isc_result_totext(result)); + } + + if (showresult) { + isc_result_t eresult; + + result = isccc_cc_lookupuint32(data, "result", &eresult); + if (result == ISC_R_SUCCESS) { + printf("%s %u\n", isc_result_toid(eresult), eresult); + } else { + printf("NONE -1\n"); + } + } + + isccc_sexpr_free(&response); + + REQUIRE(recvdone_handle == handle); + isc_nmhandle_detach(&recvdone_handle); + + if (atomic_fetch_sub_release(&recvs, 1) == 1 && + atomic_load_acquire(&sends) == 0) + { + shuttingdown = true; + isc_task_detach(&rndc_task); + isc_app_shutdown(); + } +} + +static void +rndc_recvnonce(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg; + isccc_sexpr_t *response = NULL; + isc_nmhandle_t *sendhandle = NULL; + isccc_sexpr_t *_ctrl = NULL; + isccc_region_t source; + uint32_t nonce; + isccc_sexpr_t *request = NULL; + isccc_time_t now; + isc_region_t r; + isccc_sexpr_t *data = NULL; + isc_buffer_t b; + + REQUIRE(ccmsg != NULL); + + if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) { + atomic_fetch_sub_release(&recvs, 1); + if (handle != NULL) { + REQUIRE(recvnonce_handle == handle); + isc_nmhandle_detach(&recvnonce_handle); + } + return; + } else if (result == ISC_R_EOF) { + fatal("connection to remote host closed.\n" + "* This may indicate that the\n" + "* remote server is using an older\n" + "* version of the command protocol,\n" + "* this host is not authorized to connect,\n" + "* the clocks are not synchronized,\n" + "* the key signing algorithm is incorrect\n" + "* or the key is invalid."); + } else if (result != ISC_R_SUCCESS) { + fatal("recv failed: %s", isc_result_totext(result)); + } + + source.rstart = isc_buffer_base(ccmsg->buffer); + source.rend = isc_buffer_used(ccmsg->buffer); + + DO("parse message", + isccc_cc_fromwire(&source, &response, algorithm, &secret)); + + _ctrl = isccc_alist_lookup(response, "_ctrl"); + if (!isccc_alist_alistp(_ctrl)) { + fatal("bad or missing ctrl section in response"); + } + nonce = 0; + if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) { + nonce = 0; + } + + isc_stdtime_get(&now); + + DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, + now, now + 60, &request)); + data = isccc_alist_lookup(request, "_data"); + if (data == NULL) { + fatal("_data section missing"); + } + if (isccc_cc_definestring(data, "type", args) == NULL) { + fatal("out of memory"); + } + if (nonce != 0) { + _ctrl = isccc_alist_lookup(request, "_ctrl"); + if (_ctrl == NULL) { + fatal("_ctrl section missing"); + } + if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) { + fatal("out of memory"); + } + } + + isc_buffer_clear(databuf); + /* Skip the length field (4 bytes) */ + isc_buffer_add(databuf, 4); + + DO("render message", + isccc_cc_towire(request, &databuf, algorithm, &secret)); + + isc_buffer_init(&b, databuf->base, 4); + isc_buffer_putuint32(&b, databuf->used - 4); + + r.base = databuf->base; + r.length = databuf->used; + + isc_nmhandle_attach(handle, &recvdone_handle); + atomic_fetch_add_relaxed(&recvs, 1); + isccc_ccmsg_readmessage(ccmsg, rndc_recvdone, ccmsg); + + isc_nmhandle_attach(handle, &sendhandle); + atomic_fetch_add_relaxed(&sends, 1); + isc_nm_send(handle, &r, rndc_senddone, sendhandle); + + REQUIRE(recvnonce_handle == handle); + isc_nmhandle_detach(&recvnonce_handle); + atomic_fetch_sub_release(&recvs, 1); + + isccc_sexpr_free(&response); + isccc_sexpr_free(&request); + return; +} + +static void +rndc_connected(isc_nmhandle_t *handle, isc_result_t result, void *arg) { + isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg; + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isccc_sexpr_t *request = NULL; + isccc_sexpr_t *data = NULL; + isccc_time_t now; + isc_region_t r; + isc_buffer_t b; + isc_nmhandle_t *connhandle = NULL; + isc_nmhandle_t *sendhandle = NULL; + + REQUIRE(ccmsg != NULL); + + if (result != ISC_R_SUCCESS) { + atomic_fetch_sub_release(&connects, 1); + isc_sockaddr_format(&serveraddrs[currentaddr], socktext, + sizeof(socktext)); + if (++currentaddr < nserveraddrs) { + notify("connection failed: %s: %s", socktext, + isc_result_totext(result)); + rndc_startconnect(&serveraddrs[currentaddr]); + return; + } + + fatal("connect failed: %s: %s", socktext, + isc_result_totext(result)); + } + + isc_nmhandle_attach(handle, &connhandle); + + isc_stdtime_get(&now); + DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, + now, now + 60, &request)); + data = isccc_alist_lookup(request, "_data"); + if (data == NULL) { + fatal("_data section missing"); + } + if (isccc_cc_definestring(data, "type", "null") == NULL) { + fatal("out of memory"); + } + + isc_buffer_clear(databuf); + /* Skip the length field (4 bytes) */ + isc_buffer_add(databuf, 4); + + DO("render message", + isccc_cc_towire(request, &databuf, algorithm, &secret)); + + isc_buffer_init(&b, databuf->base, 4); + isc_buffer_putuint32(&b, databuf->used - 4); + + r.base = databuf->base; + r.length = databuf->used; + + isccc_ccmsg_init(rndc_mctx, handle, ccmsg); + isccc_ccmsg_setmaxsize(ccmsg, 1024 * 1024); + + isc_nmhandle_attach(handle, &recvnonce_handle); + atomic_fetch_add_relaxed(&recvs, 1); + isccc_ccmsg_readmessage(ccmsg, rndc_recvnonce, ccmsg); + + isc_nmhandle_attach(handle, &sendhandle); + atomic_fetch_add_relaxed(&sends, 1); + isc_nm_send(handle, &r, rndc_senddone, sendhandle); + + isc_nmhandle_detach(&connhandle); + atomic_fetch_sub_release(&connects, 1); + + isccc_sexpr_free(&request); +} + +static void +rndc_startconnect(isc_sockaddr_t *addr) { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t *local = NULL; + + isc_sockaddr_format(addr, socktext, sizeof(socktext)); + + notify("using server %s (%s)", servername, socktext); + + switch (isc_sockaddr_pf(addr)) { + case AF_INET: + local = &local4; + break; + case AF_INET6: + local = &local6; + break; + case AF_UNIX: + /* + * TODO: support UNIX domain sockets in netgmr. + */ + fatal("UNIX domain sockets not currently supported"); + default: + UNREACHABLE(); + } + + atomic_fetch_add_relaxed(&connects, 1); + isc_nm_tcpconnect(netmgr, local, addr, rndc_connected, &rndc_ccmsg, + RNDC_TIMEOUT, 0); +} + +static void +rndc_start(isc_task_t *task, isc_event_t *event) { + isc_event_free(&event); + + UNUSED(task); + + currentaddr = 0; + rndc_startconnect(&serveraddrs[currentaddr]); +} + +static void +parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname, + cfg_parser_t **pctxp, cfg_obj_t **configp) { + isc_result_t result; + const char *conffile = admin_conffile; + const cfg_obj_t *addresses = NULL; + const cfg_obj_t *defkey = NULL; + const cfg_obj_t *options = NULL; + const cfg_obj_t *servers = NULL; + const cfg_obj_t *server = NULL; + const cfg_obj_t *keys = NULL; + const cfg_obj_t *key = NULL; + const cfg_obj_t *defport = NULL; + const cfg_obj_t *secretobj = NULL; + const cfg_obj_t *algorithmobj = NULL; + cfg_obj_t *config = NULL; + const cfg_obj_t *address = NULL; + const cfg_listelt_t *elt; + const char *secretstr; + const char *algorithmstr; + static char secretarray[1024]; + const cfg_type_t *conftype = &cfg_type_rndcconf; + bool key_only = false; + const cfg_listelt_t *element; + + if (!isc_file_exists(conffile)) { + conffile = admin_keyfile; + conftype = &cfg_type_rndckey; + + if (c_flag) { + fatal("%s does not exist", admin_conffile); + } + + if (!isc_file_exists(conffile)) { + fatal("neither %s nor %s was found", admin_conffile, + admin_keyfile); + } + key_only = true; + } else if (!c_flag && isc_file_exists(admin_keyfile)) { + fprintf(stderr, + "WARNING: key file (%s) exists, but using " + "default configuration file (%s)\n", + admin_keyfile, admin_conffile); + } + + DO("create parser", cfg_parser_create(mctx, log, pctxp)); + + /* + * The parser will output its own errors, so DO() is not used. + */ + result = cfg_parse_file(*pctxp, conffile, conftype, &config); + if (result != ISC_R_SUCCESS) { + fatal("could not load rndc configuration"); + } + + if (!key_only) { + (void)cfg_map_get(config, "options", &options); + } + + if (key_only && servername == NULL) { + servername = "127.0.0.1"; + } else if (servername == NULL && options != NULL) { + const cfg_obj_t *defserverobj = NULL; + (void)cfg_map_get(options, "default-server", &defserverobj); + if (defserverobj != NULL) { + servername = cfg_obj_asstring(defserverobj); + } + } + + if (servername == NULL) { + fatal("no server specified and no default"); + } + + if (!key_only) { + (void)cfg_map_get(config, "server", &servers); + if (servers != NULL) { + for (elt = cfg_list_first(servers); elt != NULL; + elt = cfg_list_next(elt)) + { + const char *name = NULL; + server = cfg_listelt_value(elt); + name = cfg_obj_asstring( + cfg_map_getname(server)); + if (strcasecmp(name, servername) == 0) { + break; + } + server = NULL; + } + } + } + + /* + * Look for the name of the key to use. + */ + if (keyname != NULL) { + /* Was set on command line, do nothing. */ + } else if (server != NULL) { + DO("get key for server", cfg_map_get(server, "key", &defkey)); + keyname = cfg_obj_asstring(defkey); + } else if (options != NULL) { + DO("get default key", + cfg_map_get(options, "default-key", &defkey)); + keyname = cfg_obj_asstring(defkey); + } else if (!key_only) { + fatal("no key for server and no default"); + } + + /* + * Get the key's definition. + */ + if (key_only) { + DO("get key", cfg_map_get(config, "key", &key)); + } else { + DO("get config key list", cfg_map_get(config, "key", &keys)); + for (elt = cfg_list_first(keys); elt != NULL; + elt = cfg_list_next(elt)) + { + const char *name = NULL; + + key = cfg_listelt_value(elt); + name = cfg_obj_asstring(cfg_map_getname(key)); + if (strcasecmp(name, keyname) == 0) { + break; + } + } + if (elt == NULL) { + fatal("no key definition for name %s", keyname); + } + } + (void)cfg_map_get(key, "secret", &secretobj); + (void)cfg_map_get(key, "algorithm", &algorithmobj); + if (secretobj == NULL || algorithmobj == NULL) { + fatal("key must have algorithm and secret"); + } + + secretstr = cfg_obj_asstring(secretobj); + algorithmstr = cfg_obj_asstring(algorithmobj); + + if (strcasecmp(algorithmstr, "hmac-md5") == 0) { + algorithm = ISCCC_ALG_HMACMD5; + } else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) { + algorithm = ISCCC_ALG_HMACSHA1; + } else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) { + algorithm = ISCCC_ALG_HMACSHA224; + } else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) { + algorithm = ISCCC_ALG_HMACSHA256; + } else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) { + algorithm = ISCCC_ALG_HMACSHA384; + } else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) { + algorithm = ISCCC_ALG_HMACSHA512; + } else { + fatal("unsupported algorithm: %s", algorithmstr); + } + + secret.rstart = (unsigned char *)secretarray; + secret.rend = (unsigned char *)secretarray + sizeof(secretarray); + DO("decode base64 secret", isccc_base64_decode(secretstr, &secret)); + secret.rend = secret.rstart; + secret.rstart = (unsigned char *)secretarray; + + /* + * Find the port to connect to. + */ + if (remoteport != 0) { + /* Was set on command line, do nothing. */ + } else { + if (server != NULL) { + (void)cfg_map_get(server, "port", &defport); + } + if (defport == NULL && options != NULL) { + (void)cfg_map_get(options, "default-port", &defport); + } + } + if (defport != NULL) { + remoteport = cfg_obj_asuint32(defport); + if (remoteport > 65535 || remoteport == 0) { + fatal("port %u out of range", remoteport); + } + } else if (remoteport == 0) { + remoteport = NS_CONTROL_PORT; + } + + if (server != NULL) { + result = cfg_map_get(server, "addresses", &addresses); + } else { + result = ISC_R_NOTFOUND; + } + if (result == ISC_R_SUCCESS) { + for (element = cfg_list_first(addresses); element != NULL; + element = cfg_list_next(element)) + { + isc_sockaddr_t sa; + + address = cfg_listelt_value(element); + if (!cfg_obj_issockaddr(address)) { + unsigned int myport; + const char *name; + const cfg_obj_t *obj; + + obj = cfg_tuple_get(address, "name"); + name = cfg_obj_asstring(obj); + obj = cfg_tuple_get(address, "port"); + if (cfg_obj_isuint32(obj)) { + myport = cfg_obj_asuint32(obj); + if (myport > UINT16_MAX || myport == 0) + { + fatal("port %u out of range", + myport); + } + } else { + myport = remoteport; + } + if (nserveraddrs < SERVERADDRS) { + get_addresses(name, (in_port_t)myport); + } else { + fprintf(stderr, + "too many address: " + "%s: dropped\n", + name); + } + continue; + } + sa = *cfg_obj_assockaddr(address); + if (isc_sockaddr_getport(&sa) == 0) { + isc_sockaddr_setport(&sa, remoteport); + } + if (nserveraddrs < SERVERADDRS) { + serveraddrs[nserveraddrs++] = sa; + } else { + char socktext[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(&sa, socktext, + sizeof(socktext)); + fprintf(stderr, + "too many address: %s: dropped\n", + socktext); + } + } + } + + if (!local4set && server != NULL) { + address = NULL; + cfg_map_get(server, "source-address", &address); + if (address != NULL) { + local4 = *cfg_obj_assockaddr(address); + local4set = true; + } + } + if (!local4set && options != NULL) { + address = NULL; + cfg_map_get(options, "default-source-address", &address); + if (address != NULL) { + local4 = *cfg_obj_assockaddr(address); + local4set = true; + } + } + + if (!local6set && server != NULL) { + address = NULL; + cfg_map_get(server, "source-address-v6", &address); + if (address != NULL) { + local6 = *cfg_obj_assockaddr(address); + local6set = true; + } + } + if (!local6set && options != NULL) { + address = NULL; + cfg_map_get(options, "default-source-address-v6", &address); + if (address != NULL) { + local6 = *cfg_obj_assockaddr(address); + local6set = true; + } + } + + *configp = config; +} + +int +main(int argc, char **argv) { + isc_result_t result = ISC_R_SUCCESS; + bool show_final_mem = false; + isc_log_t *log = NULL; + isc_logconfig_t *logconfig = NULL; + isc_logdestination_t logdest; + cfg_parser_t *pctx = NULL; + cfg_obj_t *config = NULL; + const char *keyname = NULL; + struct in_addr in; + struct in6_addr in6; + char *p; + size_t argslen; + int ch; + int i; + + result = isc_file_progname(*argv, program, sizeof(program)); + if (result != ISC_R_SUCCESS) { + memmove(program, "rndc", 5); + } + progname = program; + + admin_conffile = RNDC_CONFFILE; + admin_keyfile = RNDC_KEYFILE; + + isc_sockaddr_any(&local4); + isc_sockaddr_any6(&local6); + + result = isc_app_start(); + if (result != ISC_R_SUCCESS) { + fatal("isc_app_start() failed: %s", isc_result_totext(result)); + } + + isc_commandline_errprint = false; + + preparse_args(argc, argv); + + while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) { + switch (ch) { + case '4': + if (isc_net_probeipv4() != ISC_R_SUCCESS) { + fatal("can't find IPv4 networking"); + } + isc_net_disableipv6(); + break; + case '6': + if (isc_net_probeipv6() != ISC_R_SUCCESS) { + fatal("can't find IPv6 networking"); + } + isc_net_disableipv4(); + break; + case 'b': + if (inet_pton(AF_INET, isc_commandline_argument, &in) == + 1) + { + isc_sockaddr_fromin(&local4, &in, 0); + local4set = true; + } else if (inet_pton(AF_INET6, isc_commandline_argument, + &in6) == 1) + { + isc_sockaddr_fromin6(&local6, &in6, 0); + local6set = true; + } + break; + + case 'c': + admin_conffile = isc_commandline_argument; + c_flag = true; + break; + + case 'k': + admin_keyfile = isc_commandline_argument; + break; + + case 'M': + isc_mem_debugging = ISC_MEM_DEBUGTRACE; + break; + + case 'm': + show_final_mem = true; + break; + + case 'p': + remoteport = atoi(isc_commandline_argument); + if (remoteport > 65535 || remoteport == 0) { + fatal("port '%s' out of range", + isc_commandline_argument); + } + break; + + case 'q': + quiet = true; + break; + + case 'r': + showresult = true; + break; + + case 's': + servername = isc_commandline_argument; + break; + + case 'V': + verbose = true; + break; + + case 'y': + keyname = isc_commandline_argument; + break; + + case '?': + if (isc_commandline_option != '?') { + fprintf(stderr, "%s: invalid argument -%c\n", + program, isc_commandline_option); + usage(1); + } + FALLTHROUGH; + case 'h': + usage(0); + break; + default: + fprintf(stderr, "%s: unhandled option -%c\n", program, + isc_commandline_option); + exit(1); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argv[0] == NULL) { + usage(1); + } else { + command = argv[0]; + if (strcmp(command, "restart") == 0) { + fatal("'%s' is not implemented", command); + } + notify("%s", command); + } + + serial = isc_random32(); + + isc_mem_create(&rndc_mctx); + isc_managers_create(rndc_mctx, 1, 0, &netmgr, &taskmgr, NULL); + DO("create task", isc_task_create(taskmgr, 0, &rndc_task)); + + isc_nm_settimeouts(netmgr, RNDC_TIMEOUT, RNDC_TIMEOUT, RNDC_TIMEOUT, 0); + + isc_log_create(rndc_mctx, &log, &logconfig); + isc_log_setcontext(log); + isc_log_settag(logconfig, progname); + logdest.file.stream = stderr; + logdest.file.name = NULL; + logdest.file.versions = ISC_LOG_ROLLNEVER; + logdest.file.maximum_size = 0; + isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC, + ISC_LOG_INFO, &logdest, + ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL); + DO("enabling log channel", + isc_log_usechannel(logconfig, "stderr", NULL, NULL)); + + parse_config(rndc_mctx, log, keyname, &pctx, &config); + + isc_buffer_allocate(rndc_mctx, &databuf, 2048); + + /* + * Convert argc/argv into a space-delimited command string + * similar to what the user might enter in interactive mode + * (if that were implemented). + */ + argslen = 0; + for (i = 0; i < argc; i++) { + argslen += strlen(argv[i]) + 1; + } + + args = isc_mem_get(rndc_mctx, argslen); + + p = args; + for (i = 0; i < argc; i++) { + size_t len = strlen(argv[i]); + memmove(p, argv[i], len); + p += len; + *p++ = ' '; + } + + p--; + *p++ = '\0'; + INSIST(p == args + argslen); + + if (nserveraddrs == 0 && servername != NULL) { + get_addresses(servername, (in_port_t)remoteport); + } + + DO("post event", isc_app_onrun(rndc_mctx, rndc_task, rndc_start, NULL)); + + result = isc_app_run(); + if (result != ISC_R_SUCCESS) { + fatal("isc_app_run() failed: %s", isc_result_totext(result)); + } + + isc_managers_destroy(&netmgr, &taskmgr, NULL); + + /* + * Note: when TCP connections are shut down, there will be a final + * call to the isccc callback routine with &rndc_ccmsg as its + * argument. We therefore need to delay invalidating it until + * after the netmgr is closed down. + */ + isccc_ccmsg_invalidate(&rndc_ccmsg); + + isc_log_destroy(&log); + isc_log_setcontext(NULL); + + cfg_obj_destroy(pctx, &config); + cfg_parser_destroy(&pctx); + + isc_mem_put(rndc_mctx, args, argslen); + + isc_buffer_free(&databuf); + + if (show_final_mem) { + isc_mem_stats(rndc_mctx, stderr); + } + + isc_mem_destroy(&rndc_mctx); + + if (failed) { + return (1); + } + + return (0); +} diff --git a/bin/rndc/rndc.conf.rst b/bin/rndc/rndc.conf.rst new file mode 100644 index 0000000..20e6bc9 --- /dev/null +++ b/bin/rndc/rndc.conf.rst @@ -0,0 +1,158 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: rndc.conf +.. program:: rndc.conf +.. _man_rndc.conf: + +rndc.conf - rndc configuration file +----------------------------------- + +Synopsis +~~~~~~~~ + +:program:`rndc.conf` + +Description +~~~~~~~~~~~ + +:program:`rndc.conf` is the configuration file for :iscman:`rndc`, the BIND 9 name +server control utility. This file has a similar structure and syntax to +:iscman:`named.conf`. Statements are enclosed in braces and terminated with a +semi-colon. Clauses in the statements are also semi-colon terminated. +The usual comment styles are supported: + +C style: /\* \*/ + +C++ style: // to end of line + +Unix style: # to end of line + +:program:`rndc.conf` is much simpler than :iscman:`named.conf`. The file uses three +statements: an options statement, a server statement, and a key +statement. + +The ``options`` statement contains five clauses. The ``default-server`` +clause is followed by the name or address of a name server. This host +is used when no name server is given as an argument to :iscman:`rndc`. +The ``default-key`` clause is followed by the name of a key, which is +identified by a ``key`` statement. If no ``keyid`` is provided on the +rndc command line, and no ``key`` clause is found in a matching +``server`` statement, this default key is used to authenticate the +server's commands and responses. The ``default-port`` clause is followed +by the port to connect to on the remote name server. If no ``port`` +option is provided on the rndc command line, and no ``port`` clause is +found in a matching ``server`` statement, this default port is used +to connect. The ``default-source-address`` and +``default-source-address-v6`` clauses can be used to set the IPv4 +and IPv6 source addresses respectively. + +After the ``server`` keyword, the server statement includes a string +which is the hostname or address for a name server. The statement has +three possible clauses: ``key``, ``port``, and ``addresses``. The key +name must match the name of a key statement in the file. The port number +specifies the port to connect to. If an ``addresses`` clause is supplied, +these addresses are used instead of the server name. Each address +can take an optional port. If an ``source-address`` or +``source-address-v6`` is supplied, it is used to specify the +IPv4 and IPv6 source address, respectively. + +The ``key`` statement begins with an identifying string, the name of the +key. The statement has two clauses. ``algorithm`` identifies the +authentication algorithm for :iscman:`rndc` to use; currently only HMAC-MD5 +(for compatibility), HMAC-SHA1, HMAC-SHA224, HMAC-SHA256 (default), +HMAC-SHA384, and HMAC-SHA512 are supported. This is followed by a secret +clause which contains the base-64 encoding of the algorithm's +authentication key. The base-64 string is enclosed in double quotes. + +There are two common ways to generate the base-64 string for the secret. +The BIND 9 program :iscman:`rndc-confgen` can be used to generate a random +key, or the ``mmencode`` program, also known as ``mimencode``, can be +used to generate a base-64 string from known input. ``mmencode`` does +not ship with BIND 9 but is available on many systems. See the Example +section for sample command lines for each. + +Example +~~~~~~~ + +:: + + options { + default-server localhost; + default-key samplekey; + }; + +:: + + server localhost { + key samplekey; + }; + +:: + + server testserver { + key testkey; + addresses { localhost port 5353; }; + }; + +:: + + key samplekey { + algorithm hmac-sha256; + secret "6FMfj43Osz4lyb24OIe2iGEz9lf1llJO+lz"; + }; + +:: + + key testkey { + algorithm hmac-sha256; + secret "R3HI8P6BKw9ZwXwN3VZKuQ=="; + }; + + +In the above example, :iscman:`rndc` by default uses the server at +localhost (127.0.0.1) and the key called "samplekey". Commands to the +localhost server use the "samplekey" key, which must also be defined +in the server's configuration file with the same name and secret. The +key statement indicates that "samplekey" uses the HMAC-SHA256 algorithm +and its secret clause contains the base-64 encoding of the HMAC-SHA256 +secret enclosed in double quotes. + +If :option:`rndc -s testserver ` is used, then :iscman:`rndc` connects to the server +on localhost port 5353 using the key "testkey". + +To generate a random secret with :iscman:`rndc-confgen`: + +:iscman:`rndc-confgen` + +A complete :program:`rndc.conf` file, including the randomly generated key, +is written to the standard output. Commented-out ``key`` and +``controls`` statements for :iscman:`named.conf` are also printed. + +To generate a base-64 secret with ``mmencode``: + +``echo "known plaintext for a secret" | mmencode`` + +Name Server Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The name server must be configured to accept rndc connections and to +recognize the key specified in the :program:`rndc.conf` file, using the +controls statement in :iscman:`named.conf`. See the sections on the +``controls`` statement in the BIND 9 Administrator Reference Manual for +details. + +See Also +~~~~~~~~ + +:iscman:`rndc(8) `, :iscman:`rndc-confgen(8) `, :manpage:`mmencode(1)`, BIND 9 Administrator Reference Manual. diff --git a/bin/rndc/rndc.rst b/bin/rndc/rndc.rst new file mode 100644 index 0000000..35a4f31 --- /dev/null +++ b/bin/rndc/rndc.rst @@ -0,0 +1,667 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. iscman:: rndc +.. program:: rndc +.. _man_rndc: + +rndc - name server control utility +---------------------------------- + +Synopsis +~~~~~~~~ + +:program:`rndc` [**-b** source-address] [**-c** config-file] [**-k** key-file] [**-s** server] [**-p** port] [**-q**] [**-r**] [**-V**] [**-y** server_key] [[**-4**] | [**-6**]] {command} + +Description +~~~~~~~~~~~ + +:program:`rndc` controls the operation of a name server. If :program:`rndc` is +invoked with no command line options or arguments, it prints a short +summary of the supported commands and the available options and their +arguments. + +:program:`rndc` communicates with the name server over a TCP connection, +sending commands authenticated with digital signatures. In the current +versions of :program:`rndc` and :iscman:`named`, the only supported authentication +algorithms are HMAC-MD5 (for compatibility), HMAC-SHA1, HMAC-SHA224, +HMAC-SHA256 (default), HMAC-SHA384, and HMAC-SHA512. They use a shared +secret on each end of the connection, which provides TSIG-style +authentication for the command request and the name server's response. +All commands sent over the channel must be signed by a server_key known to +the server. + +:program:`rndc` reads a configuration file to determine how to contact the name +server and decide what algorithm and key it should use. + +Options +~~~~~~~ + +.. option:: -4 + + This option indicates use of IPv4 only. + +.. option:: -6 + + This option indicates use of IPv6 only. + +.. option:: -b source-address + + This option indicates ``source-address`` as the source address for the connection to the + server. Multiple instances are permitted, to allow setting of both the + IPv4 and IPv6 source addresses. + +.. option:: -c config-file + + This option indicates ``config-file`` as the configuration file instead of the default, + |rndc_conf|. + +.. option:: -k key-file + + This option indicates ``key-file`` as the key file instead of the default, + |rndc_key|. The key in |rndc_key| is used to + authenticate commands sent to the server if the config-file does not + exist. + +.. option:: -s server + + ``server`` is the name or address of the server which matches a server + statement in the configuration file for :program:`rndc`. If no server is + supplied on the command line, the host named by the default-server + clause in the options statement of the :program:`rndc` configuration file + is used. + +.. option:: -p port + + This option instructs BIND 9 to send commands to TCP port ``port`` instead of its default control + channel port, 953. + +.. option:: -q + + This option sets quiet mode, where message text returned by the server is not printed + unless there is an error. + +.. option:: -r + + This option instructs :program:`rndc` to print the result code returned by :iscman:`named` + after executing the requested command (e.g., ISC_R_SUCCESS, + ISC_R_FAILURE, etc.). + +.. option:: -V + + This option enables verbose logging. + +.. option:: -y server_key + + This option indicates use of the key ``server_key`` from the configuration file. For control message validation to succeed, ``server_key`` must be known + by :iscman:`named` with the same algorithm and secret string. If no ``server_key`` is specified, + :program:`rndc` first looks for a key clause in the server statement of + the server being used, or if no server statement is present for that + host, then in the default-key clause of the options statement. Note that + the configuration file contains shared secrets which are used to send + authenticated control commands to name servers, and should therefore + not have general read or write access. + +Commands +~~~~~~~~ + +A list of commands supported by :program:`rndc` can be seen by running :program:`rndc` +without arguments. + +Currently supported commands are: + +.. option:: addzone zone [class [view]] configuration + + This command adds a zone while the server is running. This command requires the + ``allow-new-zones`` option to be set to ``yes``. The configuration + string specified on the command line is the zone configuration text + that would ordinarily be placed in :iscman:`named.conf`. + + The configuration is saved in a file called ``viewname.nzf`` (or, if + :iscman:`named` is compiled with liblmdb, an LMDB database file called + ``viewname.nzd``). ``viewname`` is the name of the view, unless the view + name contains characters that are incompatible with use as a file + name, in which case a cryptographic hash of the view name is used + instead. When :iscman:`named` is restarted, the file is loaded into + the view configuration so that zones that were added can persist + after a restart. + + This sample ``addzone`` command adds the zone ``example.com`` to + the default view: + + ``rndc addzone example.com '{ type primary; file "example.com.db"; };'`` + + (Note the brackets around and semi-colon after the zone configuration + text.) + + See also :option:`rndc delzone` and :option:`rndc modzone`. + +.. option:: delzone [-clean] zone [class [view]] + + This command deletes a zone while the server is running. + + If the ``-clean`` argument is specified, the zone's master file (and + journal file, if any) are deleted along with the zone. Without + the ``-clean`` option, zone files must be deleted manually. (If the + zone is of type ``secondary`` or ``stub``, the files needing to be removed + are reported in the output of the ``rndc delzone`` command.) + + If the zone was originally added via ``rndc addzone``, then it is + removed permanently. However, if it was originally configured in + :iscman:`named.conf`, then that original configuration remains in place; + when the server is restarted or reconfigured, the zone is + recreated. To remove it permanently, it must also be removed from + :iscman:`named.conf`. + + See also :option:`rndc addzone` and :option:`rndc modzone`. + +.. option:: dnssec (-status | -rollover -key id [-alg algorithm] [-when time] | -checkds [-key id [-alg algorithm]] [-when time] published | withdrawn)) zone [class [view]] + + This command allows you to interact with the "dnssec-policy" of a given + zone. + + ``rndc dnssec -status`` show the DNSSEC signing state for the specified + zone. + + ``rndc dnssec -rollover`` allows you to schedule key rollover for a + specific key (overriding the original key lifetime). + + ``rndc dnssec -checkds`` informs :iscman:`named` that the DS for + a specified zone's key-signing key has been confirmed to be published + in, or withdrawn from, the parent zone. This is required in order to + complete a KSK rollover. The ``-key id`` and ``-alg algorithm`` arguments + can be used to specify a particular KSK, if necessary; if there is only + one key acting as a KSK for the zone, these arguments can be omitted. + The time of publication or withdrawal for the DS is set to the current + time by default, but can be overridden to a specific time with the + argument ``-when time``, where ``time`` is expressed in YYYYMMDDHHMMSS + notation. + +.. option:: dnstap (-reopen | -roll [number]) + + This command closes and re-opens DNSTAP output files. + + ``rndc dnstap -reopen`` allows + the output file to be renamed externally, so that :iscman:`named` can + truncate and re-open it. + + ``rndc dnstap -roll`` causes the output file + to be rolled automatically, similar to log files. The most recent + output file has ".0" appended to its name; the previous most recent + output file is moved to ".1", and so on. If ``number`` is specified, then + the number of backup log files is limited to that number. + +.. option:: dumpdb [-all | -cache | -zones | -adb | -bad | -expired | -fail] [view ...] + + This command dumps the server's caches (default) and/or zones to the dump file for + the specified views. If no view is specified, all views are dumped. + (See the ``dump-file`` option in the BIND 9 Administrator Reference + Manual.) + +.. option:: flush + + This command flushes the server's cache. + +.. option:: flushname name [view] + + This command flushes the given name from the view's DNS cache and, if applicable, + from the view's nameserver address database, bad server cache, and + SERVFAIL cache. + +.. option:: flushtree name [view] + + This command flushes the given name, and all of its subdomains, from the view's + DNS cache, address database, bad server cache, and SERVFAIL cache. + +.. option:: freeze [zone [class [view]]] + + This command suspends updates to a dynamic zone. If no zone is specified, then all + zones are suspended. This allows manual edits to be made to a zone + normally updated by dynamic update, and causes changes in the + journal file to be synced into the master file. All dynamic update + attempts are refused while the zone is frozen. + + See also :option:`rndc thaw`. + +.. option:: halt [-p] + + This command stops the server immediately. Recent changes made through dynamic + update or IXFR are not saved to the master files, but are rolled + forward from the journal files when the server is restarted. If + ``-p`` is specified, :iscman:`named`'s process ID is returned. This allows + an external process to determine when :iscman:`named` has completed + halting. + + See also :option:`rndc stop`. + +.. option:: loadkeys [zone [class [view]]] + + This command fetches all DNSSEC keys for the given zone from the key directory. If + they are within their publication period, they are merged into the + zone's DNSKEY RRset. Unlike :option:`rndc sign`, however, the zone is not + immediately re-signed by the new keys, but is allowed to + incrementally re-sign over time. + + This command requires that the zone be configured with a ``dnssec-policy``, or + that the ``auto-dnssec`` zone option be set to ``maintain``, and also requires the + zone to be configured to allow dynamic DNS. (See "Dynamic Update Policies" in + the Administrator Reference Manual for more details.) + +.. option:: managed-keys (status | refresh | sync | destroy) [class [view]] + + This command inspects and controls the "managed-keys" database which handles + :rfc:`5011` DNSSEC trust anchor maintenance. If a view is specified, these + commands are applied to that view; otherwise, they are applied to all + views. + + - When run with the ``status`` keyword, this prints the current status of + the managed-keys database. + + - When run with the ``refresh`` keyword, this forces an immediate refresh + query to be sent for all the managed keys, updating the + managed-keys database if any new keys are found, without waiting + the normal refresh interval. + + - When run with the ``sync`` keyword, this forces an immediate dump of + the managed-keys database to disk (in the file + ``managed-keys.bind`` or (``viewname.mkeys``). This synchronizes + the database with its journal file, so that the database's current + contents can be inspected visually. + + - When run with the ``destroy`` keyword, the managed-keys database + is shut down and deleted, and all key maintenance is terminated. + This command should be used only with extreme caution. + + Existing keys that are already trusted are not deleted from + memory; DNSSEC validation can continue after this command is used. + However, key maintenance operations cease until :iscman:`named` is + restarted or reconfigured, and all existing key maintenance states + are deleted. + + Running :option:`rndc reconfig` or restarting :iscman:`named` immediately + after this command causes key maintenance to be reinitialized + from scratch, just as if the server were being started for the + first time. This is primarily intended for testing, but it may + also be used, for example, to jumpstart the acquisition of new + keys in the event of a trust anchor rollover, or as a brute-force + repair for key maintenance problems. + +.. option:: modzone zone [class [view]] configuration + + This command modifies the configuration of a zone while the server is running. This + command requires the ``allow-new-zones`` option to be set to ``yes``. + As with ``addzone``, the configuration string specified on the + command line is the zone configuration text that would ordinarily be + placed in :iscman:`named.conf`. + + If the zone was originally added via :option:`rndc addzone`, the + configuration changes are recorded permanently and are still + in effect after the server is restarted or reconfigured. However, if + it was originally configured in :iscman:`named.conf`, then that original + configuration remains in place; when the server is restarted or + reconfigured, the zone reverts to its original configuration. To + make the changes permanent, it must also be modified in + :iscman:`named.conf`. + + See also :option:`rndc addzone` and :option:`rndc delzone`. + +.. option:: notify zone [class [view]] + + This command resends NOTIFY messages for the zone. + +.. option:: notrace + + This command sets the server's debugging level to 0. + + See also :option:`rndc trace`. + +.. option:: nta [(-class class | -dump | -force | -remove | -lifetime duration)] domain [view] + + This command sets a DNSSEC negative trust anchor (NTA) for ``domain``, with a + lifetime of ``duration``. The default lifetime is configured in + :iscman:`named.conf` via the ``nta-lifetime`` option, and defaults to one + hour. The lifetime cannot exceed one week. + + A negative trust anchor selectively disables DNSSEC validation for + zones that are known to be failing because of misconfiguration rather + than an attack. When data to be validated is at or below an active + NTA (and above any other configured trust anchors), :iscman:`named` + aborts the DNSSEC validation process and treats the data as insecure + rather than bogus. This continues until the NTA's lifetime has + elapsed. + + NTAs persist across restarts of the :iscman:`named` server. The NTAs for a + view are saved in a file called ``name.nta``, where ``name`` is the name + of the view; if it contains characters that are incompatible with + use as a file name, a cryptographic hash is generated from the name of + the view. + + An existing NTA can be removed by using the ``-remove`` option. + + An NTA's lifetime can be specified with the ``-lifetime`` option. + TTL-style suffixes can be used to specify the lifetime in seconds, + minutes, or hours. If the specified NTA already exists, its lifetime + is updated to the new value. Setting ``lifetime`` to zero is + equivalent to ``-remove``. + + If ``-dump`` is used, any other arguments are ignored and a list + of existing NTAs is printed. Note that this may include NTAs that are + expired but have not yet been cleaned up. + + Normally, :iscman:`named` periodically tests to see whether data below + an NTA can now be validated (see the ``nta-recheck`` option in the + Administrator Reference Manual for details). If data can be + validated, then the NTA is regarded as no longer necessary and is + allowed to expire early. The ``-force`` parameter overrides this behavior + and forces an NTA to persist for its entire lifetime, regardless of + whether data could be validated if the NTA were not present. + + The view class can be specified with ``-class``. The default is class + ``IN``, which is the only class for which DNSSEC is currently + supported. + + All of these options can be shortened, i.e., to ``-l``, ``-r``, + ``-d``, ``-f``, and ``-c``. + + Unrecognized options are treated as errors. To refer to a domain or + view name that begins with a hyphen, use a double-hyphen (--) on the + command line to indicate the end of options. + +.. option:: querylog [(on | off)] + + This command enables or disables query logging. For backward compatibility, this + command can also be used without an argument to toggle query logging + on and off. + + Query logging can also be enabled by explicitly directing the + ``queries`` ``category`` to a ``channel`` in the ``logging`` section + of :iscman:`named.conf`, or by specifying ``querylog yes;`` in the + ``options`` section of :iscman:`named.conf`. + +.. option:: reconfig + + This command reloads the configuration file and loads new zones, but does not reload + existing zone files even if they have changed. This is faster than a + full :option:`rndc reload` when there is a large number of zones, because it + avoids the need to examine the modification times of the zone files. + +.. option:: recursing + + This command dumps the list of queries :iscman:`named` is currently + recursing on, and the list of domains to which iterative queries + are currently being sent. + + The first list includes all unique clients that are waiting for + recursion to complete, including the query that is awaiting a + response and the timestamp (seconds since the Unix epoch) of + when named started processing this client query. + + The second list comprises of domains for which there are active + (or recently active) fetches in progress. It reports the number + of active fetches for each domain and the number of queries that + have been passed (allowed) or dropped (spilled) as a result of + the ``fetches-per-zone`` limit. (Note: these counters are not + cumulative over time; whenever the number of active fetches for + a domain drops to zero, the counter for that domain is deleted, + and the next time a fetch is sent to that domain, it is recreated + with the counters set to zero). + +.. option:: refresh zone [class [view]] + + This command schedules zone maintenance for the given zone. + +.. option:: reload + + This command reloads the configuration file and zones. + + .. program:: rndc reload + .. option:: zone [class [view]] + + If a zone is specified, this command reloads only the given zone. + +.. program:: rndc + +.. option:: retransfer zone [class [view]] + + This command retransfers the given secondary zone from the primary server. + + If the zone is configured to use ``inline-signing``, the signed + version of the zone is discarded; after the retransfer of the + unsigned version is complete, the signed version is regenerated + with new signatures. + +.. option:: scan + + This command scans the list of available network interfaces for changes, without + performing a full :option:`rndc reconfig` or waiting for the + ``interface-interval`` timer. + +.. option:: secroots [-] [view ...] + + This command dumps the security roots (i.e., trust anchors configured via + ``trust-anchors``, or the ``managed-keys`` or ``trusted-keys`` statements + [both deprecated], or ``dnssec-validation auto``) and negative trust anchors + for the specified views. If no view is specified, all views are + dumped. Security roots indicate whether they are configured as trusted + keys, managed keys, or initializing managed keys (managed keys that have not + yet been updated by a successful key refresh query). + + If the first argument is ``-``, then the output is returned via the + :program:`rndc` response channel and printed to the standard output. + Otherwise, it is written to the secroots dump file, which defaults to + ``named.secroots``, but can be overridden via the ``secroots-file`` + option in :iscman:`named.conf`. + + See also :option:`rndc managed-keys`. + +.. option:: serve-stale (on | off | reset | status) [class [view]] + + This command enables, disables, resets, or reports the current status of + the serving of stale answers as configured in :iscman:`named.conf`. + + If serving of stale answers is disabled by ``rndc-serve-stale off``, then it + remains disabled even if :iscman:`named` is reloaded or reconfigured. ``rndc + serve-stale reset`` restores the setting as configured in :iscman:`named.conf`. + + ``rndc serve-stale status`` reports whether caching and serving of stale + answers is currently enabled or disabled. It also reports the values of + ``stale-answer-ttl`` and ``max-stale-ttl``. + +.. option:: showzone zone [class [view]] + + This command prints the configuration of a running zone. + + See also :option:`rndc zonestatus`. + +.. option:: sign zone [class [view]] + + This command fetches all DNSSEC keys for the given zone from the key directory (see + the ``key-directory`` option in the BIND 9 Administrator Reference + Manual). If they are within their publication period, they are merged into + the zone's DNSKEY RRset. If the DNSKEY RRset is changed, then the + zone is automatically re-signed with the new key set. + + This command requires that the zone be configured with a ``dnssec-policy``, or + that the ``auto-dnssec`` zone option be set to ``allow`` or ``maintain``, + and also requires the zone to be configured to allow dynamic DNS. (See + "Dynamic Update Policies" in the BIND 9 Administrator Reference Manual for more + details.) + + See also :option:`rndc loadkeys`. + +.. option:: signing [(-list | -clear keyid/algorithm | -clear all | -nsec3param (parameters | none) | -serial value) zone [class [view]] + + This command lists, edits, or removes the DNSSEC signing-state records for the + specified zone. The status of ongoing DNSSEC operations, such as + signing or generating NSEC3 chains, is stored in the zone in the form + of DNS resource records of type ``sig-signing-type``. + ``rndc signing -list`` converts these records into a human-readable + form, indicating which keys are currently signing or have finished + signing the zone, and which NSEC3 chains are being created or + removed. + + ``rndc signing -clear`` can remove a single key (specified in the + same format that ``rndc signing -list`` uses to display it), or all + keys. In either case, only completed keys are removed; any record + indicating that a key has not yet finished signing the zone is + retained. + + ``rndc signing -nsec3param`` sets the NSEC3 parameters for a zone. + This is the only supported mechanism for using NSEC3 with + ``inline-signing`` zones. Parameters are specified in the same format + as an NSEC3PARAM resource record: ``hash algorithm``, ``flags``, ``iterations``, + and ``salt``, in that order. + + Currently, the only defined value for ``hash algorithm`` is ``1``, + representing SHA-1. The ``flags`` may be set to ``0`` or ``1``, + depending on whether the opt-out bit in the NSEC3 + chain should be set. ``iterations`` defines the number of additional times to apply + the algorithm when generating an NSEC3 hash. The ``salt`` is a string + of data expressed in hexadecimal, a hyphen (``-``) if no salt is to be + used, or the keyword ``auto``, which causes :iscman:`named` to generate a + random 64-bit salt. + + The only recommended configuration is ``rndc signing -nsec3param 1 0 0 - zone``, + i.e. no salt, no additional iterations, no opt-out. + + .. warning:: + Do not use extra iterations, salt, or opt-out unless all their implications + are fully understood. A higher number of iterations causes interoperability + problems and opens servers to CPU-exhausting DoS attacks. + + ``rndc signing -nsec3param none`` removes an existing NSEC3 chain and + replaces it with NSEC. + + ``rndc signing -serial value`` sets the serial number of the zone to + ``value``. If the value would cause the serial number to go backwards, it + is rejected. The primary use of this parameter is to set the serial number on inline + signed zones. + +.. option:: stats + + This command writes server statistics to the statistics file. (See the + ``statistics-file`` option in the BIND 9 Administrator Reference + Manual.) + +.. option:: status + + This command displays the status of the server. Note that the number of zones includes + the internal ``bind/CH`` zone and the default ``./IN`` hint zone, if + there is no explicit root zone configured. + +.. option:: stop -p + + This command stops the server, making sure any recent changes made through dynamic + update or IXFR are first saved to the master files of the updated + zones. If ``-p`` is specified, :iscman:`named`'s process ID is returned. + This allows an external process to determine when :iscman:`named` has + completed stopping. + + See also :option:`rndc halt`. + +.. option:: sync -clean [zone [class [view]]] + + This command syncs changes in the journal file for a dynamic zone to the master + file. If the "-clean" option is specified, the journal file is also + removed. If no zone is specified, then all zones are synced. + +.. option:: tcp-timeouts [initial idle keepalive advertised] + + When called without arguments, this command displays the current values of the + ``tcp-initial-timeout``, ``tcp-idle-timeout``, + ``tcp-keepalive-timeout``, and ``tcp-advertised-timeout`` options. + When called with arguments, these values are updated. This allows an + administrator to make rapid adjustments when under a + denial-of-service (DoS) attack. See the descriptions of these options in the BIND 9 + Administrator Reference Manual for details of their use. + +.. option:: thaw [zone [class [view]]] + + This command enables updates to a frozen dynamic zone. If no zone is specified, + then all frozen zones are enabled. This causes the server to reload + the zone from disk, and re-enables dynamic updates after the load has + completed. After a zone is thawed, dynamic updates are no longer + refused. If the zone has changed and the ``ixfr-from-differences`` + option is in use, the journal file is updated to reflect + changes in the zone. Otherwise, if the zone has changed, any existing + journal file is removed. + + See also :option:`rndc freeze`. + +.. option:: trace [level] + + If no level is specified, this command increments the server's debugging + level by one. + + .. program:: rndc trace + .. option:: level + + If specified, this command sets the server's debugging level to the + provided value. + + See also :option:`rndc notrace`. + +.. program:: rndc + +.. option:: tsig-delete keyname [view] + + This command deletes a given TKEY-negotiated key from the server. This does not + apply to statically configured TSIG keys. + +.. option:: tsig-list + + This command lists the names of all TSIG keys currently configured for use by + :iscman:`named` in each view. The list includes both statically configured keys and + dynamic TKEY-negotiated keys. + +.. option:: validation (on | off | status) [view ...] + + This command enables, disables, or checks the current status of DNSSEC validation. By + default, validation is enabled. + + The cache is flushed when validation is turned on or off to avoid using data + that might differ between states. + +.. option:: zonestatus zone [class [view]] + + This command displays the current status of the given zone, including the master + file name and any include files from which it was loaded, when it was + most recently loaded, the current serial number, the number of nodes, + whether the zone supports dynamic updates, whether the zone is DNSSEC + signed, whether it uses automatic DNSSEC key management or inline + signing, and the scheduled refresh or expiry times for the zone. + + See also :option:`rndc showzone`. + +:program:`rndc` commands that specify zone names, such as :option:`reload` +:option:`retransfer`, or :option:`zonestatus`, can be ambiguous when applied to zones +of type ``redirect``. Redirect zones are always called ``.``, and can be +confused with zones of type ``hint`` or with secondary copies of the root +zone. To specify a redirect zone, use the special zone name +``-redirect``, without a trailing period. (With a trailing period, this +would specify a zone called "-redirect".) + +Limitations +~~~~~~~~~~~ + +There is currently no way to provide the shared secret for a ``server_key`` +without using the configuration file. + +Several error messages could be clearer. + +See Also +~~~~~~~~ + +:iscman:`rndc.conf(5) `, :iscman:`rndc-confgen(8) `, +:iscman:`named(8) `, :iscman:`named.conf(5) `, BIND 9 Administrator +Reference Manual. diff --git a/bin/rndc/util.c b/bin/rndc/util.c new file mode 100644 index 0000000..b0085fe --- /dev/null +++ b/bin/rndc/util.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include "util.h" +#include +#include +#include +#include + +#include + +extern bool verbose; +extern const char *progname; + +void +notify(const char *fmt, ...) { + va_list ap; + + if (verbose) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } +} + +void +fatal(const char *format, ...) { + va_list args; + + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} diff --git a/bin/rndc/util.h b/bin/rndc/util.h new file mode 100644 index 0000000..4ac83e5 --- /dev/null +++ b/bin/rndc/util.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include +#include +#include + +#define NS_CONTROL_PORT 953 + +#undef DO +#define DO(name, function) \ + do { \ + result = function; \ + if (result != ISC_R_SUCCESS) \ + fatal("%s: %s", name, isc_result_totext(result)); \ + else \ + notify("%s", name); \ + } while (0) + +ISC_LANG_BEGINDECLS + +void +notify(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2); + +noreturn void +fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +ISC_LANG_ENDDECLS diff --git a/bin/tests/Makefile.am b/bin/tests/Makefile.am new file mode 100644 index 0000000..56e81b1 --- /dev/null +++ b/bin/tests/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/Makefile.top + +EXTRA_DIST = convert-trs-to-junit.py + +SUBDIRS = system + +noinst_PROGRAMS = \ + test_client \ + test_server \ + wire_test + +AM_CFLAGS += \ + $(TEST_CFLAGS) + +test_client_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) + +test_client_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +test_server_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) + +test_server_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +wire_test_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) + +wire_test_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) diff --git a/bin/tests/Makefile.in b/bin/tests/Makefile.in new file mode 100644 index 0000000..d8b1015 --- /dev/null +++ b/bin/tests/Makefile.in @@ -0,0 +1,977 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +noinst_PROGRAMS = test_client$(EXEEXT) test_server$(EXEEXT) \ + wire_test$(EXEEXT) +subdir = bin/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +test_client_SOURCES = test_client.c +test_client_OBJECTS = test_client-test_client.$(OBJEXT) +test_client_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +test_server_SOURCES = test_server.c +test_server_OBJECTS = test_server-test_server.$(OBJEXT) +test_server_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) +wire_test_SOURCES = wire_test.c +wire_test_OBJECTS = wire_test-wire_test.$(OBJEXT) +wire_test_DEPENDENCIES = $(LIBISC_LIBS) $(LIBDNS_LIBS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/test_client-test_client.Po \ + ./$(DEPDIR)/test_server-test_server.Po \ + ./$(DEPDIR)/wire_test-wire_test.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = test_client.c test_server.c wire_test.c +DIST_SOURCES = test_client.c test_server.c wire_test.c +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Makefile.top \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = $(STD_CFLAGS) $(TEST_CFLAGS) +AM_CPPFLAGS = \ + $(STD_CPPFLAGS) \ + -include $(top_builddir)/config.h \ + -I$(srcdir)/include + +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +EXTRA_DIST = convert-trs-to-junit.py +SUBDIRS = system +test_client_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) + +test_client_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +test_server_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) + +test_server_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +wire_test_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) + +wire_test_LDADD = \ + $(LIBISC_LIBS) \ + $(LIBDNS_LIBS) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +test_client$(EXEEXT): $(test_client_OBJECTS) $(test_client_DEPENDENCIES) $(EXTRA_test_client_DEPENDENCIES) + @rm -f test_client$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_client_OBJECTS) $(test_client_LDADD) $(LIBS) + +test_server$(EXEEXT): $(test_server_OBJECTS) $(test_server_DEPENDENCIES) $(EXTRA_test_server_DEPENDENCIES) + @rm -f test_server$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_server_OBJECTS) $(test_server_LDADD) $(LIBS) + +wire_test$(EXEEXT): $(wire_test_OBJECTS) $(wire_test_DEPENDENCIES) $(EXTRA_wire_test_DEPENDENCIES) + @rm -f wire_test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(wire_test_OBJECTS) $(wire_test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_client-test_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_server-test_server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wire_test-wire_test.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +test_client-test_client.o: test_client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_client_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_client-test_client.o -MD -MP -MF $(DEPDIR)/test_client-test_client.Tpo -c -o test_client-test_client.o `test -f 'test_client.c' || echo '$(srcdir)/'`test_client.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_client-test_client.Tpo $(DEPDIR)/test_client-test_client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_client.c' object='test_client-test_client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_client_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_client-test_client.o `test -f 'test_client.c' || echo '$(srcdir)/'`test_client.c + +test_client-test_client.obj: test_client.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_client_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_client-test_client.obj -MD -MP -MF $(DEPDIR)/test_client-test_client.Tpo -c -o test_client-test_client.obj `if test -f 'test_client.c'; then $(CYGPATH_W) 'test_client.c'; else $(CYGPATH_W) '$(srcdir)/test_client.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_client-test_client.Tpo $(DEPDIR)/test_client-test_client.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_client.c' object='test_client-test_client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_client_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_client-test_client.obj `if test -f 'test_client.c'; then $(CYGPATH_W) 'test_client.c'; else $(CYGPATH_W) '$(srcdir)/test_client.c'; fi` + +test_server-test_server.o: test_server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_server_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_server-test_server.o -MD -MP -MF $(DEPDIR)/test_server-test_server.Tpo -c -o test_server-test_server.o `test -f 'test_server.c' || echo '$(srcdir)/'`test_server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_server-test_server.Tpo $(DEPDIR)/test_server-test_server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_server.c' object='test_server-test_server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_server_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_server-test_server.o `test -f 'test_server.c' || echo '$(srcdir)/'`test_server.c + +test_server-test_server.obj: test_server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_server_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_server-test_server.obj -MD -MP -MF $(DEPDIR)/test_server-test_server.Tpo -c -o test_server-test_server.obj `if test -f 'test_server.c'; then $(CYGPATH_W) 'test_server.c'; else $(CYGPATH_W) '$(srcdir)/test_server.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_server-test_server.Tpo $(DEPDIR)/test_server-test_server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test_server.c' object='test_server-test_server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_server_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_server-test_server.obj `if test -f 'test_server.c'; then $(CYGPATH_W) 'test_server.c'; else $(CYGPATH_W) '$(srcdir)/test_server.c'; fi` + +wire_test-wire_test.o: wire_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wire_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wire_test-wire_test.o -MD -MP -MF $(DEPDIR)/wire_test-wire_test.Tpo -c -o wire_test-wire_test.o `test -f 'wire_test.c' || echo '$(srcdir)/'`wire_test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wire_test-wire_test.Tpo $(DEPDIR)/wire_test-wire_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wire_test.c' object='wire_test-wire_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wire_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wire_test-wire_test.o `test -f 'wire_test.c' || echo '$(srcdir)/'`wire_test.c + +wire_test-wire_test.obj: wire_test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wire_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wire_test-wire_test.obj -MD -MP -MF $(DEPDIR)/wire_test-wire_test.Tpo -c -o wire_test-wire_test.obj `if test -f 'wire_test.c'; then $(CYGPATH_W) 'wire_test.c'; else $(CYGPATH_W) '$(srcdir)/wire_test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wire_test-wire_test.Tpo $(DEPDIR)/wire_test-wire_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wire_test.c' object='wire_test-wire_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(wire_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wire_test-wire_test.obj `if test -f 'wire_test.c'; then $(CYGPATH_W) 'wire_test.c'; else $(CYGPATH_W) '$(srcdir)/wire_test.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/test_client-test_client.Po + -rm -f ./$(DEPDIR)/test_server-test_server.Po + -rm -f ./$(DEPDIR)/wire_test-wire_test.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-recursive + +doc-am: doc-local + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/test_client-test_client.Po + -rm -f ./$(DEPDIR)/test_server-test_server.Po + -rm -f ./$(DEPDIR)/wire_test-wire_test.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +test: test-recursive + +test-am: test-local + +uninstall-am: + +unit: unit-recursive + +unit-am: unit-local + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir doc-am doc-local dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am test-am test-local uninstall uninstall-am unit-am \ + unit-local + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/tests/convert-trs-to-junit.py b/bin/tests/convert-trs-to-junit.py new file mode 100755 index 0000000..85b37dd --- /dev/null +++ b/bin/tests/convert-trs-to-junit.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# Convert automake .trs files into JUnit format suitable for Gitlab + +import argparse +import os +import sys +from xml.etree import ElementTree +from xml.etree.ElementTree import Element +from xml.etree.ElementTree import SubElement + + +# getting explicit encoding specification right for Python 2/3 would be messy, +# so let's hope for the best +def read_whole_text(filename): + with open(filename) as inf: # pylint: disable-msg=unspecified-encoding + return inf.read().strip() + + +def read_trs_result(filename): + result = None + with open(filename, "r") as trs: # pylint: disable-msg=unspecified-encoding + for line in trs: + items = line.split() + if len(items) < 2: + raise ValueError("unsupported line in trs file", filename, line) + if items[0] != (":test-result:"): + continue + if result is not None: + raise NotImplementedError("double :test-result:", filename) + result = items[1].upper() + + if result is None: + raise ValueError(":test-result: not found", filename) + + return result + + +def find_test_relative_path(source_dir, in_path): + """Return {in_path}.c if it exists, with fallback to {in_path}""" + candidates_relative = [in_path + ".c", in_path] + for relative in candidates_relative: + absolute = os.path.join(source_dir, relative) + if os.path.exists(absolute): + return relative + raise KeyError + + +def err_out(exception): + raise exception + + +def walk_trss(source_dir): + for cur_dir, _dirs, files in os.walk(source_dir, onerror=err_out): + for filename in files: + if not filename.endswith(".trs"): + continue + + filename_prefix = filename[: -len(".trs")] + log_name = filename_prefix + ".log" + full_trs_path = os.path.join(cur_dir, filename) + full_log_path = os.path.join(cur_dir, log_name) + sub_dir = os.path.relpath(cur_dir, source_dir) + test_name = os.path.join(sub_dir, filename_prefix) + + t = { + "name": test_name, + "full_log_path": full_log_path, + "rel_log_path": os.path.relpath(full_log_path, source_dir), + } + t["result"] = read_trs_result(full_trs_path) + + # try to find dir/file path for a clickable link + try: + t["rel_file_path"] = find_test_relative_path(source_dir, test_name) + except KeyError: + pass # no existing path found + + yield t + + +def append_testcase(testsuite, t): + # attributes taken from + # https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/lib/gitlab/ci/parsers/test/junit.rb + attrs = {"name": t["name"]} + if "rel_file_path" in t: + attrs["file"] = t["rel_file_path"] + + testcase = SubElement(testsuite, "testcase", attrs) + + # Gitlab accepts only [[ATTACHMENT| links for system-out, not raw text + s = SubElement(testcase, "system-out") + s.text = "[[ATTACHMENT|" + t["rel_log_path"] + "]]" + if t["result"].lower() == "pass": + return + + # Gitlab shows output only for failed or skipped tests + if t["result"].lower() == "skip": + err = SubElement(testcase, "skipped") + else: + err = SubElement(testcase, "failure") + err.text = read_whole_text(t["full_log_path"]) + + +def gen_junit(results): + testsuites = Element("testsuites") + testsuite = SubElement(testsuites, "testsuite") + for test in results: + append_testcase(testsuite, test) + return testsuites + + +def check_directory(path): + try: + os.listdir(path) + return path + except OSError as ex: + msg = "Path {} cannot be listed as a directory: {}".format(path, ex) + raise argparse.ArgumentTypeError(msg) + + +def main(): + parser = argparse.ArgumentParser( + description="Recursively search for .trs + .log files and compile " + "them into JUnit XML suitable for Gitlab. Paths in the " + "XML are relative to the specified top directory." + ) + parser.add_argument( + "top_directory", + type=check_directory, + help="root directory where to start scanning for .trs files", + ) + args = parser.parse_args() + junit = gen_junit(walk_trss(args.top_directory)) + + # encode results into file format, on Python 3 it produces bytes + xml = ElementTree.tostring(junit, "utf-8") + # use stdout as a binary file object, Python2/3 compatibility + output = getattr(sys.stdout, "buffer", sys.stdout) + output.write(xml) + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/Makefile.am b/bin/tests/system/Makefile.am new file mode 100644 index 0000000..72d464a --- /dev/null +++ b/bin/tests/system/Makefile.am @@ -0,0 +1,258 @@ +include $(top_srcdir)/Makefile.top + +EXTRA_DIST = . + +# Source tarballs must not contain configure/build artifacts. +dist-hook: + git clean -n -x -d | \ + grep -v "Makefile.in$$" | \ + sed -n "s|^Would remove \(.*\)|$(distdir)/\1|p" | \ + xargs -I{} rm -rf "{}" + +SUBDIRS = dyndb/driver dlzexternal/driver hooks/driver + +AM_CPPFLAGS += \ + $(LIBISC_CFLAGS) + +LDADD += \ + $(LIBISC_LIBS) + +if HAVE_PERL + +noinst_PROGRAMS = \ + feature-test \ + makejournal \ + pipelined/pipequeries \ + resolve \ + rndc/gencheck \ + rpz/dnsrps \ + tkey/keycreate \ + tkey/keydelete + +feature_test_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +makejournal_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +makejournal_LDADD = \ + $(LDADD) \ + $(LIBDNS_LIBS) + +pipelined_pipequeries_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +pipelined_pipequeries_LDADD = \ + $(LDADD) \ + $(LIBDNS_LIBS) + +resolve_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBISC_CFLAGS) \ + $(LIBDNS_CFLAGS) \ + $(LIBIRS_CFLAGS) + +resolve_LDADD = $(LIBISC_LIBS) $(LIBIRS_LIBS) $(LIBDNS_LIBS) + +rpz_dnsrps_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +rpz_dnsrps_LDADD = \ + $(LDADD) \ + $(LIBDNS_LIBS) + +tkey_keycreate_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +tkey_keycreate_LDADD = \ + $(LDADD) \ + $(LIBDNS_LIBS) + +tkey_keydelete_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBDNS_CFLAGS) + +tkey_keydelete_LDADD = \ + $(LDADD) \ + $(LIBDNS_LIBS) + +TESTS = + +if HAVE_PERLMOD_TIME_HIRES +TESTS += serve-stale +endif HAVE_PERLMOD_TIME_HIRES + +if HAVE_PERLMOD_NET_DNS +TESTS += \ + rpzrecurse +endif HAVE_PERLMOD_NET_DNS + +if HAVE_LIBNGHTTP2 +TESTS += \ + doth +endif + +TESTS += \ + acl \ + additional \ + addzone \ + allow-query \ + auth \ + autosign \ + builtin \ + cacheclean \ + case \ + catz \ + cds \ + checkconf \ + checknames \ + checkzone \ + database \ + dialup \ + dlzexternal \ + dns64 \ + dsdigest \ + dupsigs \ + dyndb \ + ecdsa \ + eddsa \ + ednscompliance \ + emptyzones \ + enginepkcs11 \ + filter-aaaa \ + formerr \ + geoip2 \ + glue \ + idna \ + include-multiplecfg \ + inline \ + integrity \ + hooks \ + host \ + journal \ + keepalive \ + keyfromlabel \ + legacy \ + limits \ + logfileconfig \ + masterfile \ + masterformat \ + metadata \ + mirror \ + mkeys \ + names \ + notify \ + nsec3 \ + nslookup \ + padding \ + pending \ + redirect \ + rndc \ + rootkeysentinel \ + rpz \ + rrchecker \ + rrl \ + rrsetorder \ + rsabigexponent \ + runtime \ + sfcache \ + smartsign \ + sortlist \ + spf \ + staticstub \ + stub \ + synthfromdnssec \ + tkey \ + tools \ + transport-acl \ + tsig \ + tsiggss \ + ttl \ + unknown \ + verify \ + views \ + wildcard \ + xferquota \ + zonechecks + +if HAVE_LMDB +TESTS += nzd2nzf +endif # HAVE_LMDB + +if HAVE_PERLMOD_NET_DNS + +TESTS += \ + fetchlimit \ + ixfr \ + nsupdate \ + resolver \ + statistics \ + stress \ + upforwd \ + zero + +if HAVE_DNSTAP +TESTS += dnstap +endif + +if HAVE_PERLMOD_FILE_FETCH +TESTS += statschannel +endif HAVE_PERLMOD_FILE_FETCH + +if HAVE_PERLMOD_DIGEST_HMAC +TESTS += xfer +endif HAVE_PERLMOD_DIGEST_HMAC + +if HAVE_PERLMOD_NET_DNS_NAMESERVER +TESTS += reclimit +endif HAVE_PERLMOD_NET_DNS_NAMESERVER + +endif HAVE_PERLMOD_NET_DNS + +if HAVE_PYTHON +TESTS += kasp keymgr2kasp tcp pipelined + +if HAVE_PYTEST +TESTS += checkds dispatch rpzextra shutdown timeouts +endif + +if HAVE_PYMOD_DNS +TESTS += qmin cookie +if HAVE_PERLMOD_NET_DNS +TESTS += digdelv dnssec forward +if HAVE_PERLMOD_NET_DNS_NAMESERVER +TESTS += chain +endif HAVE_PERLMOD_NET_DNS_NAMESERVER +endif HAVE_PERLMOD_NET_DNS +endif HAVE_PYMOD_DNS + +endif HAVE_PYTHON + +else !HAVE_PERL +check: + echo Perl is not available, no tests were ran + exit 1 +endif !HAVE_PERL + +LOG_DRIVER_V = $(LOG_DRIVER_V_@AM_V@) +LOG_DRIVER_V_ = $(LOG_DRIVER_V_@AM_DEFAULT_V@) +LOG_DRIVER_V_0 = --verbose no +LOG_DRIVER_V_1 = --verbose yes + +LOG_DRIVER = $(srcdir)/custom-test-driver +AM_LOG_DRIVER_FLAGS = $(LOG_DRIVER_V) + +LOG_COMPILER = $(builddir)/legacy.run.sh +AM_LOG_FLAGS = -r + +$(TESTS): legacy.run.sh + +test-local: check + +clean-local:: + -find $(builddir) -maxdepth 1 -type d -name "*_*" | xargs rm -rf diff --git a/bin/tests/system/Makefile.in b/bin/tests/system/Makefile.in new file mode 100644 index 0000000..eb62072 --- /dev/null +++ b/bin/tests/system/Makefile.in @@ -0,0 +1,2416 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Hey Emacs, this is -*- makefile-automake -*- file! +# vim: filetype=automake + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@HOST_MACOS_TRUE@am__append_1 = \ +@HOST_MACOS_TRUE@ -Wl,-flat_namespace + +@HAVE_PERL_TRUE@noinst_PROGRAMS = feature-test$(EXEEXT) \ +@HAVE_PERL_TRUE@ makejournal$(EXEEXT) \ +@HAVE_PERL_TRUE@ pipelined/pipequeries$(EXEEXT) \ +@HAVE_PERL_TRUE@ resolve$(EXEEXT) rndc/gencheck$(EXEEXT) \ +@HAVE_PERL_TRUE@ rpz/dnsrps$(EXEEXT) tkey/keycreate$(EXEEXT) \ +@HAVE_PERL_TRUE@ tkey/keydelete$(EXEEXT) +@HAVE_PERLMOD_TIME_HIRES_TRUE@@HAVE_PERL_TRUE@am__append_2 = serve-stale +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_3 = \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ rpzrecurse + +@HAVE_LIBNGHTTP2_TRUE@@HAVE_PERL_TRUE@am__append_4 = \ +@HAVE_LIBNGHTTP2_TRUE@@HAVE_PERL_TRUE@ doth + +@HAVE_LMDB_TRUE@@HAVE_PERL_TRUE@am__append_5 = nzd2nzf +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_6 = \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ fetchlimit \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ ixfr \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ nsupdate \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ resolver \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ statistics \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ stress \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ upforwd \ +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@ zero + +@HAVE_DNSTAP_TRUE@@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_7 = dnstap +@HAVE_PERLMOD_FILE_FETCH_TRUE@@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_8 = statschannel +@HAVE_PERLMOD_DIGEST_HMAC_TRUE@@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_9 = xfer +@HAVE_PERLMOD_NET_DNS_NAMESERVER_TRUE@@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@am__append_10 = reclimit +@HAVE_PERL_TRUE@@HAVE_PYTHON_TRUE@am__append_11 = kasp keymgr2kasp tcp pipelined +@HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@am__append_12 = checkds dispatch rpzextra shutdown timeouts +@HAVE_PERL_TRUE@@HAVE_PYMOD_DNS_TRUE@@HAVE_PYTHON_TRUE@am__append_13 = qmin cookie +@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@@HAVE_PYMOD_DNS_TRUE@@HAVE_PYTHON_TRUE@am__append_14 = digdelv dnssec forward +@HAVE_PERLMOD_NET_DNS_NAMESERVER_TRUE@@HAVE_PERLMOD_NET_DNS_TRUE@@HAVE_PERL_TRUE@@HAVE_PYMOD_DNS_TRUE@@HAVE_PYTHON_TRUE@am__append_15 = chain +subdir = bin/tests/system +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_check_openssl.m4 \ + $(top_srcdir)/m4/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4/ax_jemalloc.m4 \ + $(top_srcdir)/m4/ax_lib_lmdb.m4 \ + $(top_srcdir)/m4/ax_perl_module.m4 \ + $(top_srcdir)/m4/ax_posix_shell.m4 \ + $(top_srcdir)/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_python_module.m4 \ + $(top_srcdir)/m4/ax_restore_flags.m4 \ + $(top_srcdir)/m4/ax_save_flags.m4 $(top_srcdir)/m4/ax_tls.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = conf.sh ifconfig.sh legacy.run.sh start.sh \ + stop.sh +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +feature_test_SOURCES = feature-test.c +feature_test_OBJECTS = feature_test-feature-test.$(OBJEXT) +feature_test_LDADD = $(LDADD) +feature_test_DEPENDENCIES = $(LIBISC_LIBS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +makejournal_SOURCES = makejournal.c +makejournal_OBJECTS = makejournal-makejournal.$(OBJEXT) +@HAVE_PERL_TRUE@makejournal_DEPENDENCIES = $(LDADD) $(LIBDNS_LIBS) +pipelined_pipequeries_SOURCES = pipelined/pipequeries.c +am__dirstamp = $(am__leading_dot)dirstamp +pipelined_pipequeries_OBJECTS = \ + pipelined/pipequeries-pipequeries.$(OBJEXT) +@HAVE_PERL_TRUE@pipelined_pipequeries_DEPENDENCIES = $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) +resolve_SOURCES = resolve.c +resolve_OBJECTS = resolve-resolve.$(OBJEXT) +@HAVE_PERL_TRUE@resolve_DEPENDENCIES = $(LIBISC_LIBS) $(LIBIRS_LIBS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) +rndc_gencheck_SOURCES = rndc/gencheck.c +rndc_gencheck_OBJECTS = rndc/gencheck.$(OBJEXT) +rndc_gencheck_LDADD = $(LDADD) +rndc_gencheck_DEPENDENCIES = $(LIBISC_LIBS) +rpz_dnsrps_SOURCES = rpz/dnsrps.c +rpz_dnsrps_OBJECTS = rpz/dnsrps-dnsrps.$(OBJEXT) +@HAVE_PERL_TRUE@rpz_dnsrps_DEPENDENCIES = $(LDADD) $(LIBDNS_LIBS) +tkey_keycreate_SOURCES = tkey/keycreate.c +tkey_keycreate_OBJECTS = tkey/keycreate-keycreate.$(OBJEXT) +@HAVE_PERL_TRUE@tkey_keycreate_DEPENDENCIES = $(LDADD) $(LIBDNS_LIBS) +tkey_keydelete_SOURCES = tkey/keydelete.c +tkey_keydelete_OBJECTS = tkey/keydelete-keydelete.$(OBJEXT) +@HAVE_PERL_TRUE@tkey_keydelete_DEPENDENCIES = $(LDADD) $(LIBDNS_LIBS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/feature_test-feature-test.Po \ + ./$(DEPDIR)/makejournal-makejournal.Po \ + ./$(DEPDIR)/resolve-resolve.Po \ + pipelined/$(DEPDIR)/pipequeries-pipequeries.Po \ + rndc/$(DEPDIR)/gencheck.Po rpz/$(DEPDIR)/dnsrps-dnsrps.Po \ + tkey/$(DEPDIR)/keycreate-keycreate.Po \ + tkey/$(DEPDIR)/keydelete-keydelete.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = feature-test.c makejournal.c pipelined/pipequeries.c \ + resolve.c rndc/gencheck.c rpz/dnsrps.c tkey/keycreate.c \ + tkey/keydelete.c +DIST_SOURCES = feature-test.c makejournal.c pipelined/pipequeries.c \ + resolve.c rndc/gencheck.c rpz/dnsrps.c tkey/keycreate.c \ + tkey/keydelete.c +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir distdir-am +am__extra_recursive_targets = test-recursive unit-recursive \ + doc-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/conf.sh.in \ + $(srcdir)/ifconfig.sh.in $(srcdir)/legacy.run.sh.in \ + $(srcdir)/start.sh.in $(srcdir)/stop.sh.in \ + $(top_srcdir)/Makefile.top $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CURL = @CURL@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEVELOPER_MODE = @DEVELOPER_MODE@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +FSTRM_CAPTURE = @FSTRM_CAPTURE@ +FUZZ_LDFLAGS = @FUZZ_LDFLAGS@ +FUZZ_LOG_COMPILER = @FUZZ_LOG_COMPILER@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +JEMALLOC_CFLAGS = @JEMALLOC_CFLAGS@ +JEMALLOC_LIBS = @JEMALLOC_LIBS@ +JSON_C_CFLAGS = @JSON_C_CFLAGS@ +JSON_C_LIBS = @JSON_C_LIBS@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_CONFIG = @KRB5_CONFIG@ +KRB5_LIBS = @KRB5_LIBS@ +LATEXMK = @LATEXMK@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBCAP_LIBS = @LIBCAP_LIBS@ +LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@ +LIBIDN2_LIBS = @LIBIDN2_LIBS@ +LIBNGHTTP2_CFLAGS = @LIBNGHTTP2_CFLAGS@ +LIBNGHTTP2_LIBS = @LIBNGHTTP2_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUV_CFLAGS = @LIBUV_CFLAGS@ +LIBUV_LIBS = @LIBUV_LIBS@ +LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ +LIBXML2_LIBS = @LIBXML2_LIBS@ +LIPO = @LIPO@ +LMDB_CFLAGS = @LMDB_CFLAGS@ +LMDB_LIBS = @LMDB_LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAXMINDDB_CFLAGS = @MAXMINDDB_CFLAGS@ +MAXMINDDB_LIBS = @MAXMINDDB_LIBS@ +MAXMINDDB_PREFIX = @MAXMINDDB_PREFIX@ +MKDIR_P = @MKDIR_P@ +NC = @NC@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTEST = @PYTEST@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +READLINE_CFLAGS = @READLINE_CFLAGS@ +READLINE_LIBS = @READLINE_LIBS@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINX_BUILD = @SPHINX_BUILD@ +STD_CFLAGS = @STD_CFLAGS@ +STD_CPPFLAGS = @STD_CPPFLAGS@ +STD_LDFLAGS = @STD_LDFLAGS@ +STRIP = @STRIP@ +TEST_CFLAGS = @TEST_CFLAGS@ +VERSION = @VERSION@ +XELATEX = @XELATEX@ +XSLTPROC = @XSLTPROC@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I $(top_srcdir)/m4 +AM_CFLAGS = \ + $(STD_CFLAGS) + +AM_CPPFLAGS = $(STD_CPPFLAGS) -include $(top_builddir)/config.h \ + -I$(srcdir)/include $(LIBISC_CFLAGS) +AM_LDFLAGS = $(STD_LDFLAGS) $(am__append_1) +LDADD = $(LIBISC_LIBS) +LIBISC_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/isc/include \ + -I$(top_builddir)/lib/isc/include + +LIBISC_LIBS = $(top_builddir)/lib/isc/libisc.la +LIBDNS_CFLAGS = \ + -I$(top_srcdir)/lib/dns/include \ + -I$(top_builddir)/lib/dns/include + +LIBDNS_LIBS = \ + $(top_builddir)/lib/dns/libdns.la + +LIBNS_CFLAGS = \ + -I$(top_srcdir)/lib/ns/include + +LIBNS_LIBS = \ + $(top_builddir)/lib/ns/libns.la + +LIBIRS_CFLAGS = \ + -I$(top_srcdir)/lib/irs/include + +LIBIRS_LIBS = \ + $(top_builddir)/lib/irs/libirs.la + +LIBISCCFG_CFLAGS = \ + -I$(top_srcdir)/lib/isccfg/include + +LIBISCCFG_LIBS = \ + $(top_builddir)/lib/isccfg/libisccfg.la + +LIBISCCC_CFLAGS = \ + -I$(top_srcdir)/lib/isccc/include/ + +LIBISCCC_LIBS = \ + $(top_builddir)/lib/isccc/libisccc.la + +LIBBIND9_CFLAGS = \ + -I$(top_srcdir)/lib/bind9/include + +LIBBIND9_LIBS = \ + $(top_builddir)/lib/bind9/libbind9.la + +EXTRA_DIST = . +SUBDIRS = dyndb/driver dlzexternal/driver hooks/driver +@HAVE_PERL_TRUE@feature_test_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@makejournal_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@makejournal_LDADD = \ +@HAVE_PERL_TRUE@ $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) + +@HAVE_PERL_TRUE@pipelined_pipequeries_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@pipelined_pipequeries_LDADD = \ +@HAVE_PERL_TRUE@ $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) + +@HAVE_PERL_TRUE@resolve_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBISC_CFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBIRS_CFLAGS) + +@HAVE_PERL_TRUE@resolve_LDADD = $(LIBISC_LIBS) $(LIBIRS_LIBS) $(LIBDNS_LIBS) +@HAVE_PERL_TRUE@rpz_dnsrps_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@rpz_dnsrps_LDADD = \ +@HAVE_PERL_TRUE@ $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) + +@HAVE_PERL_TRUE@tkey_keycreate_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@tkey_keycreate_LDADD = \ +@HAVE_PERL_TRUE@ $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) + +@HAVE_PERL_TRUE@tkey_keydelete_CPPFLAGS = \ +@HAVE_PERL_TRUE@ $(AM_CPPFLAGS) \ +@HAVE_PERL_TRUE@ $(LIBDNS_CFLAGS) + +@HAVE_PERL_TRUE@tkey_keydelete_LDADD = \ +@HAVE_PERL_TRUE@ $(LDADD) \ +@HAVE_PERL_TRUE@ $(LIBDNS_LIBS) + +@HAVE_PERL_TRUE@TESTS = $(am__append_2) $(am__append_3) \ +@HAVE_PERL_TRUE@ $(am__append_4) acl additional addzone \ +@HAVE_PERL_TRUE@ allow-query auth autosign builtin cacheclean \ +@HAVE_PERL_TRUE@ case catz cds checkconf checknames checkzone \ +@HAVE_PERL_TRUE@ database dialup dlzexternal dns64 dsdigest \ +@HAVE_PERL_TRUE@ dupsigs dyndb ecdsa eddsa ednscompliance \ +@HAVE_PERL_TRUE@ emptyzones enginepkcs11 filter-aaaa formerr \ +@HAVE_PERL_TRUE@ geoip2 glue idna include-multiplecfg inline \ +@HAVE_PERL_TRUE@ integrity hooks host journal keepalive \ +@HAVE_PERL_TRUE@ keyfromlabel legacy limits logfileconfig \ +@HAVE_PERL_TRUE@ masterfile masterformat metadata mirror mkeys \ +@HAVE_PERL_TRUE@ names notify nsec3 nslookup padding pending \ +@HAVE_PERL_TRUE@ redirect rndc rootkeysentinel rpz rrchecker \ +@HAVE_PERL_TRUE@ rrl rrsetorder rsabigexponent runtime sfcache \ +@HAVE_PERL_TRUE@ smartsign sortlist spf staticstub stub \ +@HAVE_PERL_TRUE@ synthfromdnssec tkey tools transport-acl tsig \ +@HAVE_PERL_TRUE@ tsiggss ttl unknown verify views wildcard \ +@HAVE_PERL_TRUE@ xferquota zonechecks $(am__append_5) \ +@HAVE_PERL_TRUE@ $(am__append_6) $(am__append_7) \ +@HAVE_PERL_TRUE@ $(am__append_8) $(am__append_9) \ +@HAVE_PERL_TRUE@ $(am__append_10) $(am__append_11) \ +@HAVE_PERL_TRUE@ $(am__append_12) $(am__append_13) \ +@HAVE_PERL_TRUE@ $(am__append_14) $(am__append_15) +LOG_DRIVER_V = $(LOG_DRIVER_V_@AM_V@) +LOG_DRIVER_V_ = $(LOG_DRIVER_V_@AM_DEFAULT_V@) +LOG_DRIVER_V_0 = --verbose no +LOG_DRIVER_V_1 = --verbose yes +LOG_DRIVER = $(srcdir)/custom-test-driver +AM_LOG_DRIVER_FLAGS = $(LOG_DRIVER_V) +LOG_COMPILER = $(builddir)/legacy.run.sh +AM_LOG_FLAGS = -r +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.top $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/tests/system/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign bin/tests/system/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Makefile.top $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +conf.sh: $(top_builddir)/config.status $(srcdir)/conf.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ifconfig.sh: $(top_builddir)/config.status $(srcdir)/ifconfig.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +legacy.run.sh: $(top_builddir)/config.status $(srcdir)/legacy.run.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +start.sh: $(top_builddir)/config.status $(srcdir)/start.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +stop.sh: $(top_builddir)/config.status $(srcdir)/stop.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +feature-test$(EXEEXT): $(feature_test_OBJECTS) $(feature_test_DEPENDENCIES) $(EXTRA_feature_test_DEPENDENCIES) + @rm -f feature-test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(feature_test_OBJECTS) $(feature_test_LDADD) $(LIBS) + +makejournal$(EXEEXT): $(makejournal_OBJECTS) $(makejournal_DEPENDENCIES) $(EXTRA_makejournal_DEPENDENCIES) + @rm -f makejournal$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(makejournal_OBJECTS) $(makejournal_LDADD) $(LIBS) +pipelined/$(am__dirstamp): + @$(MKDIR_P) pipelined + @: > pipelined/$(am__dirstamp) +pipelined/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) pipelined/$(DEPDIR) + @: > pipelined/$(DEPDIR)/$(am__dirstamp) +pipelined/pipequeries-pipequeries.$(OBJEXT): \ + pipelined/$(am__dirstamp) pipelined/$(DEPDIR)/$(am__dirstamp) + +pipelined/pipequeries$(EXEEXT): $(pipelined_pipequeries_OBJECTS) $(pipelined_pipequeries_DEPENDENCIES) $(EXTRA_pipelined_pipequeries_DEPENDENCIES) pipelined/$(am__dirstamp) + @rm -f pipelined/pipequeries$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(pipelined_pipequeries_OBJECTS) $(pipelined_pipequeries_LDADD) $(LIBS) + +resolve$(EXEEXT): $(resolve_OBJECTS) $(resolve_DEPENDENCIES) $(EXTRA_resolve_DEPENDENCIES) + @rm -f resolve$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(resolve_OBJECTS) $(resolve_LDADD) $(LIBS) +rndc/$(am__dirstamp): + @$(MKDIR_P) rndc + @: > rndc/$(am__dirstamp) +rndc/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) rndc/$(DEPDIR) + @: > rndc/$(DEPDIR)/$(am__dirstamp) +rndc/gencheck.$(OBJEXT): rndc/$(am__dirstamp) \ + rndc/$(DEPDIR)/$(am__dirstamp) + +rndc/gencheck$(EXEEXT): $(rndc_gencheck_OBJECTS) $(rndc_gencheck_DEPENDENCIES) $(EXTRA_rndc_gencheck_DEPENDENCIES) rndc/$(am__dirstamp) + @rm -f rndc/gencheck$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rndc_gencheck_OBJECTS) $(rndc_gencheck_LDADD) $(LIBS) +rpz/$(am__dirstamp): + @$(MKDIR_P) rpz + @: > rpz/$(am__dirstamp) +rpz/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) rpz/$(DEPDIR) + @: > rpz/$(DEPDIR)/$(am__dirstamp) +rpz/dnsrps-dnsrps.$(OBJEXT): rpz/$(am__dirstamp) \ + rpz/$(DEPDIR)/$(am__dirstamp) + +rpz/dnsrps$(EXEEXT): $(rpz_dnsrps_OBJECTS) $(rpz_dnsrps_DEPENDENCIES) $(EXTRA_rpz_dnsrps_DEPENDENCIES) rpz/$(am__dirstamp) + @rm -f rpz/dnsrps$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rpz_dnsrps_OBJECTS) $(rpz_dnsrps_LDADD) $(LIBS) +tkey/$(am__dirstamp): + @$(MKDIR_P) tkey + @: > tkey/$(am__dirstamp) +tkey/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) tkey/$(DEPDIR) + @: > tkey/$(DEPDIR)/$(am__dirstamp) +tkey/keycreate-keycreate.$(OBJEXT): tkey/$(am__dirstamp) \ + tkey/$(DEPDIR)/$(am__dirstamp) + +tkey/keycreate$(EXEEXT): $(tkey_keycreate_OBJECTS) $(tkey_keycreate_DEPENDENCIES) $(EXTRA_tkey_keycreate_DEPENDENCIES) tkey/$(am__dirstamp) + @rm -f tkey/keycreate$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tkey_keycreate_OBJECTS) $(tkey_keycreate_LDADD) $(LIBS) +tkey/keydelete-keydelete.$(OBJEXT): tkey/$(am__dirstamp) \ + tkey/$(DEPDIR)/$(am__dirstamp) + +tkey/keydelete$(EXEEXT): $(tkey_keydelete_OBJECTS) $(tkey_keydelete_DEPENDENCIES) $(EXTRA_tkey_keydelete_DEPENDENCIES) tkey/$(am__dirstamp) + @rm -f tkey/keydelete$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tkey_keydelete_OBJECTS) $(tkey_keydelete_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f pipelined/*.$(OBJEXT) + -rm -f rndc/*.$(OBJEXT) + -rm -f rpz/*.$(OBJEXT) + -rm -f tkey/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/feature_test-feature-test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makejournal-makejournal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve-resolve.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@pipelined/$(DEPDIR)/pipequeries-pipequeries.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@rndc/$(DEPDIR)/gencheck.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@rpz/$(DEPDIR)/dnsrps-dnsrps.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@tkey/$(DEPDIR)/keycreate-keycreate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@tkey/$(DEPDIR)/keydelete-keydelete.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +feature_test-feature-test.o: feature-test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(feature_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT feature_test-feature-test.o -MD -MP -MF $(DEPDIR)/feature_test-feature-test.Tpo -c -o feature_test-feature-test.o `test -f 'feature-test.c' || echo '$(srcdir)/'`feature-test.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/feature_test-feature-test.Tpo $(DEPDIR)/feature_test-feature-test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='feature-test.c' object='feature_test-feature-test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(feature_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o feature_test-feature-test.o `test -f 'feature-test.c' || echo '$(srcdir)/'`feature-test.c + +feature_test-feature-test.obj: feature-test.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(feature_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT feature_test-feature-test.obj -MD -MP -MF $(DEPDIR)/feature_test-feature-test.Tpo -c -o feature_test-feature-test.obj `if test -f 'feature-test.c'; then $(CYGPATH_W) 'feature-test.c'; else $(CYGPATH_W) '$(srcdir)/feature-test.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/feature_test-feature-test.Tpo $(DEPDIR)/feature_test-feature-test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='feature-test.c' object='feature_test-feature-test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(feature_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o feature_test-feature-test.obj `if test -f 'feature-test.c'; then $(CYGPATH_W) 'feature-test.c'; else $(CYGPATH_W) '$(srcdir)/feature-test.c'; fi` + +makejournal-makejournal.o: makejournal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(makejournal_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT makejournal-makejournal.o -MD -MP -MF $(DEPDIR)/makejournal-makejournal.Tpo -c -o makejournal-makejournal.o `test -f 'makejournal.c' || echo '$(srcdir)/'`makejournal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/makejournal-makejournal.Tpo $(DEPDIR)/makejournal-makejournal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='makejournal.c' object='makejournal-makejournal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(makejournal_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o makejournal-makejournal.o `test -f 'makejournal.c' || echo '$(srcdir)/'`makejournal.c + +makejournal-makejournal.obj: makejournal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(makejournal_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT makejournal-makejournal.obj -MD -MP -MF $(DEPDIR)/makejournal-makejournal.Tpo -c -o makejournal-makejournal.obj `if test -f 'makejournal.c'; then $(CYGPATH_W) 'makejournal.c'; else $(CYGPATH_W) '$(srcdir)/makejournal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/makejournal-makejournal.Tpo $(DEPDIR)/makejournal-makejournal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='makejournal.c' object='makejournal-makejournal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(makejournal_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o makejournal-makejournal.obj `if test -f 'makejournal.c'; then $(CYGPATH_W) 'makejournal.c'; else $(CYGPATH_W) '$(srcdir)/makejournal.c'; fi` + +pipelined/pipequeries-pipequeries.o: pipelined/pipequeries.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(pipelined_pipequeries_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pipelined/pipequeries-pipequeries.o -MD -MP -MF pipelined/$(DEPDIR)/pipequeries-pipequeries.Tpo -c -o pipelined/pipequeries-pipequeries.o `test -f 'pipelined/pipequeries.c' || echo '$(srcdir)/'`pipelined/pipequeries.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pipelined/$(DEPDIR)/pipequeries-pipequeries.Tpo pipelined/$(DEPDIR)/pipequeries-pipequeries.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pipelined/pipequeries.c' object='pipelined/pipequeries-pipequeries.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(pipelined_pipequeries_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pipelined/pipequeries-pipequeries.o `test -f 'pipelined/pipequeries.c' || echo '$(srcdir)/'`pipelined/pipequeries.c + +pipelined/pipequeries-pipequeries.obj: pipelined/pipequeries.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(pipelined_pipequeries_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pipelined/pipequeries-pipequeries.obj -MD -MP -MF pipelined/$(DEPDIR)/pipequeries-pipequeries.Tpo -c -o pipelined/pipequeries-pipequeries.obj `if test -f 'pipelined/pipequeries.c'; then $(CYGPATH_W) 'pipelined/pipequeries.c'; else $(CYGPATH_W) '$(srcdir)/pipelined/pipequeries.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pipelined/$(DEPDIR)/pipequeries-pipequeries.Tpo pipelined/$(DEPDIR)/pipequeries-pipequeries.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pipelined/pipequeries.c' object='pipelined/pipequeries-pipequeries.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(pipelined_pipequeries_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pipelined/pipequeries-pipequeries.obj `if test -f 'pipelined/pipequeries.c'; then $(CYGPATH_W) 'pipelined/pipequeries.c'; else $(CYGPATH_W) '$(srcdir)/pipelined/pipequeries.c'; fi` + +resolve-resolve.o: resolve.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(resolve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT resolve-resolve.o -MD -MP -MF $(DEPDIR)/resolve-resolve.Tpo -c -o resolve-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-resolve.Tpo $(DEPDIR)/resolve-resolve.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='resolve-resolve.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(resolve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o resolve-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c + +resolve-resolve.obj: resolve.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(resolve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT resolve-resolve.obj -MD -MP -MF $(DEPDIR)/resolve-resolve.Tpo -c -o resolve-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/resolve-resolve.Tpo $(DEPDIR)/resolve-resolve.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='resolve-resolve.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(resolve_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o resolve-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi` + +rpz/dnsrps-dnsrps.o: rpz/dnsrps.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rpz_dnsrps_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rpz/dnsrps-dnsrps.o -MD -MP -MF rpz/$(DEPDIR)/dnsrps-dnsrps.Tpo -c -o rpz/dnsrps-dnsrps.o `test -f 'rpz/dnsrps.c' || echo '$(srcdir)/'`rpz/dnsrps.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) rpz/$(DEPDIR)/dnsrps-dnsrps.Tpo rpz/$(DEPDIR)/dnsrps-dnsrps.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpz/dnsrps.c' object='rpz/dnsrps-dnsrps.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rpz_dnsrps_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rpz/dnsrps-dnsrps.o `test -f 'rpz/dnsrps.c' || echo '$(srcdir)/'`rpz/dnsrps.c + +rpz/dnsrps-dnsrps.obj: rpz/dnsrps.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rpz_dnsrps_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT rpz/dnsrps-dnsrps.obj -MD -MP -MF rpz/$(DEPDIR)/dnsrps-dnsrps.Tpo -c -o rpz/dnsrps-dnsrps.obj `if test -f 'rpz/dnsrps.c'; then $(CYGPATH_W) 'rpz/dnsrps.c'; else $(CYGPATH_W) '$(srcdir)/rpz/dnsrps.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) rpz/$(DEPDIR)/dnsrps-dnsrps.Tpo rpz/$(DEPDIR)/dnsrps-dnsrps.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpz/dnsrps.c' object='rpz/dnsrps-dnsrps.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(rpz_dnsrps_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o rpz/dnsrps-dnsrps.obj `if test -f 'rpz/dnsrps.c'; then $(CYGPATH_W) 'rpz/dnsrps.c'; else $(CYGPATH_W) '$(srcdir)/rpz/dnsrps.c'; fi` + +tkey/keycreate-keycreate.o: tkey/keycreate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keycreate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tkey/keycreate-keycreate.o -MD -MP -MF tkey/$(DEPDIR)/keycreate-keycreate.Tpo -c -o tkey/keycreate-keycreate.o `test -f 'tkey/keycreate.c' || echo '$(srcdir)/'`tkey/keycreate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) tkey/$(DEPDIR)/keycreate-keycreate.Tpo tkey/$(DEPDIR)/keycreate-keycreate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tkey/keycreate.c' object='tkey/keycreate-keycreate.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keycreate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tkey/keycreate-keycreate.o `test -f 'tkey/keycreate.c' || echo '$(srcdir)/'`tkey/keycreate.c + +tkey/keycreate-keycreate.obj: tkey/keycreate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keycreate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tkey/keycreate-keycreate.obj -MD -MP -MF tkey/$(DEPDIR)/keycreate-keycreate.Tpo -c -o tkey/keycreate-keycreate.obj `if test -f 'tkey/keycreate.c'; then $(CYGPATH_W) 'tkey/keycreate.c'; else $(CYGPATH_W) '$(srcdir)/tkey/keycreate.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) tkey/$(DEPDIR)/keycreate-keycreate.Tpo tkey/$(DEPDIR)/keycreate-keycreate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tkey/keycreate.c' object='tkey/keycreate-keycreate.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keycreate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tkey/keycreate-keycreate.obj `if test -f 'tkey/keycreate.c'; then $(CYGPATH_W) 'tkey/keycreate.c'; else $(CYGPATH_W) '$(srcdir)/tkey/keycreate.c'; fi` + +tkey/keydelete-keydelete.o: tkey/keydelete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keydelete_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tkey/keydelete-keydelete.o -MD -MP -MF tkey/$(DEPDIR)/keydelete-keydelete.Tpo -c -o tkey/keydelete-keydelete.o `test -f 'tkey/keydelete.c' || echo '$(srcdir)/'`tkey/keydelete.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) tkey/$(DEPDIR)/keydelete-keydelete.Tpo tkey/$(DEPDIR)/keydelete-keydelete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tkey/keydelete.c' object='tkey/keydelete-keydelete.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keydelete_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tkey/keydelete-keydelete.o `test -f 'tkey/keydelete.c' || echo '$(srcdir)/'`tkey/keydelete.c + +tkey/keydelete-keydelete.obj: tkey/keydelete.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keydelete_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tkey/keydelete-keydelete.obj -MD -MP -MF tkey/$(DEPDIR)/keydelete-keydelete.Tpo -c -o tkey/keydelete-keydelete.obj `if test -f 'tkey/keydelete.c'; then $(CYGPATH_W) 'tkey/keydelete.c'; else $(CYGPATH_W) '$(srcdir)/tkey/keydelete.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) tkey/$(DEPDIR)/keydelete-keydelete.Tpo tkey/$(DEPDIR)/keydelete-keydelete.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tkey/keydelete.c' object='tkey/keydelete-keydelete.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tkey_keydelete_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tkey/keydelete-keydelete.obj `if test -f 'tkey/keydelete.c'; then $(CYGPATH_W) 'tkey/keydelete.c'; else $(CYGPATH_W) '$(srcdir)/tkey/keydelete.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf pipelined/.libs pipelined/_libs + -rm -rf rndc/.libs rndc/_libs + -rm -rf rpz/.libs rpz/_libs + -rm -rf tkey/.libs tkey/_libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" +test-local: +unit-local: +doc-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +serve-stale.log: serve-stale + @p='serve-stale'; \ + b='serve-stale'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rpzrecurse.log: rpzrecurse + @p='rpzrecurse'; \ + b='rpzrecurse'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +doth.log: doth + @p='doth'; \ + b='doth'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +acl.log: acl + @p='acl'; \ + b='acl'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +additional.log: additional + @p='additional'; \ + b='additional'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +addzone.log: addzone + @p='addzone'; \ + b='addzone'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +allow-query.log: allow-query + @p='allow-query'; \ + b='allow-query'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +auth.log: auth + @p='auth'; \ + b='auth'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +autosign.log: autosign + @p='autosign'; \ + b='autosign'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +builtin.log: builtin + @p='builtin'; \ + b='builtin'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +cacheclean.log: cacheclean + @p='cacheclean'; \ + b='cacheclean'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +case.log: case + @p='case'; \ + b='case'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +catz.log: catz + @p='catz'; \ + b='catz'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +cds.log: cds + @p='cds'; \ + b='cds'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +checkconf.log: checkconf + @p='checkconf'; \ + b='checkconf'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +checknames.log: checknames + @p='checknames'; \ + b='checknames'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +checkzone.log: checkzone + @p='checkzone'; \ + b='checkzone'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +database.log: database + @p='database'; \ + b='database'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dialup.log: dialup + @p='dialup'; \ + b='dialup'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dlzexternal.log: dlzexternal + @p='dlzexternal'; \ + b='dlzexternal'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dns64.log: dns64 + @p='dns64'; \ + b='dns64'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dsdigest.log: dsdigest + @p='dsdigest'; \ + b='dsdigest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dupsigs.log: dupsigs + @p='dupsigs'; \ + b='dupsigs'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dyndb.log: dyndb + @p='dyndb'; \ + b='dyndb'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +ecdsa.log: ecdsa + @p='ecdsa'; \ + b='ecdsa'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +eddsa.log: eddsa + @p='eddsa'; \ + b='eddsa'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +ednscompliance.log: ednscompliance + @p='ednscompliance'; \ + b='ednscompliance'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +emptyzones.log: emptyzones + @p='emptyzones'; \ + b='emptyzones'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +enginepkcs11.log: enginepkcs11 + @p='enginepkcs11'; \ + b='enginepkcs11'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +filter-aaaa.log: filter-aaaa + @p='filter-aaaa'; \ + b='filter-aaaa'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +formerr.log: formerr + @p='formerr'; \ + b='formerr'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +geoip2.log: geoip2 + @p='geoip2'; \ + b='geoip2'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +glue.log: glue + @p='glue'; \ + b='glue'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +idna.log: idna + @p='idna'; \ + b='idna'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +include-multiplecfg.log: include-multiplecfg + @p='include-multiplecfg'; \ + b='include-multiplecfg'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +inline.log: inline + @p='inline'; \ + b='inline'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +integrity.log: integrity + @p='integrity'; \ + b='integrity'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +hooks.log: hooks + @p='hooks'; \ + b='hooks'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +host.log: host + @p='host'; \ + b='host'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +journal.log: journal + @p='journal'; \ + b='journal'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +keepalive.log: keepalive + @p='keepalive'; \ + b='keepalive'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +keyfromlabel.log: keyfromlabel + @p='keyfromlabel'; \ + b='keyfromlabel'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +legacy.log: legacy + @p='legacy'; \ + b='legacy'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +limits.log: limits + @p='limits'; \ + b='limits'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +logfileconfig.log: logfileconfig + @p='logfileconfig'; \ + b='logfileconfig'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +masterfile.log: masterfile + @p='masterfile'; \ + b='masterfile'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +masterformat.log: masterformat + @p='masterformat'; \ + b='masterformat'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +metadata.log: metadata + @p='metadata'; \ + b='metadata'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mirror.log: mirror + @p='mirror'; \ + b='mirror'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +mkeys.log: mkeys + @p='mkeys'; \ + b='mkeys'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +names.log: names + @p='names'; \ + b='names'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +notify.log: notify + @p='notify'; \ + b='notify'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +nsec3.log: nsec3 + @p='nsec3'; \ + b='nsec3'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +nslookup.log: nslookup + @p='nslookup'; \ + b='nslookup'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +padding.log: padding + @p='padding'; \ + b='padding'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +pending.log: pending + @p='pending'; \ + b='pending'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +redirect.log: redirect + @p='redirect'; \ + b='redirect'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rndc.log: rndc + @p='rndc'; \ + b='rndc'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rootkeysentinel.log: rootkeysentinel + @p='rootkeysentinel'; \ + b='rootkeysentinel'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rpz.log: rpz + @p='rpz'; \ + b='rpz'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rrchecker.log: rrchecker + @p='rrchecker'; \ + b='rrchecker'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rrl.log: rrl + @p='rrl'; \ + b='rrl'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rrsetorder.log: rrsetorder + @p='rrsetorder'; \ + b='rrsetorder'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rsabigexponent.log: rsabigexponent + @p='rsabigexponent'; \ + b='rsabigexponent'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +runtime.log: runtime + @p='runtime'; \ + b='runtime'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +sfcache.log: sfcache + @p='sfcache'; \ + b='sfcache'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +smartsign.log: smartsign + @p='smartsign'; \ + b='smartsign'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +sortlist.log: sortlist + @p='sortlist'; \ + b='sortlist'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +spf.log: spf + @p='spf'; \ + b='spf'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +staticstub.log: staticstub + @p='staticstub'; \ + b='staticstub'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +stub.log: stub + @p='stub'; \ + b='stub'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +synthfromdnssec.log: synthfromdnssec + @p='synthfromdnssec'; \ + b='synthfromdnssec'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +tkey.log: tkey + @p='tkey'; \ + b='tkey'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +tools.log: tools + @p='tools'; \ + b='tools'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +transport-acl.log: transport-acl + @p='transport-acl'; \ + b='transport-acl'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +tsig.log: tsig + @p='tsig'; \ + b='tsig'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +tsiggss.log: tsiggss + @p='tsiggss'; \ + b='tsiggss'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +ttl.log: ttl + @p='ttl'; \ + b='ttl'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +unknown.log: unknown + @p='unknown'; \ + b='unknown'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +verify.log: verify + @p='verify'; \ + b='verify'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +views.log: views + @p='views'; \ + b='views'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +wildcard.log: wildcard + @p='wildcard'; \ + b='wildcard'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +xferquota.log: xferquota + @p='xferquota'; \ + b='xferquota'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +zonechecks.log: zonechecks + @p='zonechecks'; \ + b='zonechecks'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +nzd2nzf.log: nzd2nzf + @p='nzd2nzf'; \ + b='nzd2nzf'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +fetchlimit.log: fetchlimit + @p='fetchlimit'; \ + b='fetchlimit'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +ixfr.log: ixfr + @p='ixfr'; \ + b='ixfr'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +nsupdate.log: nsupdate + @p='nsupdate'; \ + b='nsupdate'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +resolver.log: resolver + @p='resolver'; \ + b='resolver'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +statistics.log: statistics + @p='statistics'; \ + b='statistics'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +stress.log: stress + @p='stress'; \ + b='stress'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +upforwd.log: upforwd + @p='upforwd'; \ + b='upforwd'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +zero.log: zero + @p='zero'; \ + b='zero'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dnstap.log: dnstap + @p='dnstap'; \ + b='dnstap'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +statschannel.log: statschannel + @p='statschannel'; \ + b='statschannel'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +xfer.log: xfer + @p='xfer'; \ + b='xfer'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +reclimit.log: reclimit + @p='reclimit'; \ + b='reclimit'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +kasp.log: kasp + @p='kasp'; \ + b='kasp'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +keymgr2kasp.log: keymgr2kasp + @p='keymgr2kasp'; \ + b='keymgr2kasp'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +tcp.log: tcp + @p='tcp'; \ + b='tcp'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +pipelined.log: pipelined + @p='pipelined'; \ + b='pipelined'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +checkds.log: checkds + @p='checkds'; \ + b='checkds'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dispatch.log: dispatch + @p='dispatch'; \ + b='dispatch'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +rpzextra.log: rpzextra + @p='rpzextra'; \ + b='rpzextra'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +shutdown.log: shutdown + @p='shutdown'; \ + b='shutdown'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +timeouts.log: timeouts + @p='timeouts'; \ + b='timeouts'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +qmin.log: qmin + @p='qmin'; \ + b='qmin'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +cookie.log: cookie + @p='cookie'; \ + b='cookie'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +digdelv.log: digdelv + @p='digdelv'; \ + b='digdelv'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dnssec.log: dnssec + @p='dnssec'; \ + b='dnssec'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +forward.log: forward + @p='forward'; \ + b='forward'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +chain.log: chain + @p='chain'; \ + b='chain'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f pipelined/$(DEPDIR)/$(am__dirstamp) + -rm -f pipelined/$(am__dirstamp) + -rm -f rndc/$(DEPDIR)/$(am__dirstamp) + -rm -f rndc/$(am__dirstamp) + -rm -f rpz/$(DEPDIR)/$(am__dirstamp) + -rm -f rpz/$(am__dirstamp) + -rm -f tkey/$(DEPDIR)/$(am__dirstamp) + -rm -f tkey/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/feature_test-feature-test.Po + -rm -f ./$(DEPDIR)/makejournal-makejournal.Po + -rm -f ./$(DEPDIR)/resolve-resolve.Po + -rm -f pipelined/$(DEPDIR)/pipequeries-pipequeries.Po + -rm -f rndc/$(DEPDIR)/gencheck.Po + -rm -f rpz/$(DEPDIR)/dnsrps-dnsrps.Po + -rm -f tkey/$(DEPDIR)/keycreate-keycreate.Po + -rm -f tkey/$(DEPDIR)/keydelete-keydelete.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +doc: doc-recursive + +doc-am: doc-local + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/feature_test-feature-test.Po + -rm -f ./$(DEPDIR)/makejournal-makejournal.Po + -rm -f ./$(DEPDIR)/resolve-resolve.Po + -rm -f pipelined/$(DEPDIR)/pipequeries-pipequeries.Po + -rm -f rndc/$(DEPDIR)/gencheck.Po + -rm -f rpz/$(DEPDIR)/dnsrps-dnsrps.Po + -rm -f tkey/$(DEPDIR)/keycreate-keycreate.Po + -rm -f tkey/$(DEPDIR)/keydelete-keydelete.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +test: test-recursive + +test-am: test-local + +uninstall-am: + +unit: unit-recursive + +unit-am: unit-local + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-local clean-noinstPROGRAMS cscopelist-am \ + ctags ctags-am dist-hook distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir \ + doc-am doc-local dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + test-am test-local uninstall uninstall-am unit-am unit-local + +.PRECIOUS: Makefile + + +# Source tarballs must not contain configure/build artifacts. +dist-hook: + git clean -n -x -d | \ + grep -v "Makefile.in$$" | \ + sed -n "s|^Would remove \(.*\)|$(distdir)/\1|p" | \ + xargs -I{} rm -rf "{}" + +@HAVE_PERL_FALSE@check: +@HAVE_PERL_FALSE@ echo Perl is not available, no tests were ran +@HAVE_PERL_FALSE@ exit 1 + +$(TESTS): legacy.run.sh + +test-local: check + +clean-local:: + -find $(builddir) -maxdepth 1 -type d -name "*_*" | xargs rm -rf + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/bin/tests/system/README b/bin/tests/system/README new file mode 100644 index 0000000..265a9ef --- /dev/null +++ b/bin/tests/system/README @@ -0,0 +1,843 @@ +Copyright (C) Internet Systems Consortium, Inc. ("ISC") + +SPDX-License-Identifier: MPL-2.0 + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, you can obtain one at https://mozilla.org/MPL/2.0/. + +See the COPYRIGHT file distributed with this work for additional +information regarding copyright ownership. + +Introduction +=== +This directory holds a simple test environment for running bind9 system tests +involving multiple name servers. + +With the exception of "common" (which holds configuration information common to +multiple tests), each directory holds a set of scripts and configuration +files to test different parts of BIND. The directories are named for the +aspect of BIND they test, for example: + + dnssec/ DNSSEC tests + forward/ Forwarding tests + glue/ Glue handling tests + +etc. + +Typically each set of tests sets up 2-5 name servers and then performs one or +more tests against them. Within the test subdirectory, each name server has a +separate subdirectory containing its configuration data. These subdirectories +are named "nsN" or "ansN" (where N is a number between 1 and 8, e.g. ns1, ans2 +etc.) + +The tests are completely self-contained and do not require access to the real +DNS. Generally, one of the test servers (usually ns1) is set up as a root +nameserver and is listed in the hints file of the others. + + +Preparing to Run the Tests +=== +To enable all servers to run on the same machine, they bind to separate virtual +IP addresses on the loopback interface. ns1 runs on 10.53.0.1, ns2 on +10.53.0.2, etc. Before running any tests, you must set up these addresses by +running the command + + sh ifconfig.sh up + +as root. The interfaces can be removed by executing the command: + + sh ifconfig.sh down + +... also as root. + +The servers use unprivileged ports (above 1024) instead of the usual port 53, +so they can be run without root privileges once the interfaces have been set +up. + + +Note for MacOS Users +--- +If you wish to make the interfaces survive across reboots, copy +org.isc.bind.system and org.isc.bind.system.plist to /Library/LaunchDaemons +then run + + launchctl load /Library/LaunchDaemons/org.isc.bind.system.plist + +... as root. + + +Running the System Tests with pytest +=== + +The pytest system test runner is currently in development, but it is the +recommended way to run tests. Please report issues to QA. + +Running an Individual Test +--- + +pytest -k + +Note that in comparison to the legacy test runner, some additional tests might +be picked up when specifying just the system test directory name. To check +which tests will be executed, you can use the `--collect-only` option. You +might also be able to find a more specific test name to provide to ensure only +your desired test is executed. See help for `-k` option in `pytest --help` for +more info. + +It is also possible to run a single individual pytest test case. For example, +you can use the name test_sslyze_dot to execute just the test_sslyze_dot() +function from doth/tests_sslyze.py. The entire needed setup and teardown will +be handled by the framework. + +Running All the System Tests +--- + +Issuing plain `pytest` command without any argument will execute all tests +sequenatially. To execute them in parallel, ensure you have pytest-xdist +installed and run: + +pytest -n + + +Running the System Tests Using the Legacy Runner +=== + +!!! WARNING !!! +--- +The legacy way to run system tests is currently being reworked into a pytest +system test runner described in the previous section. The contents of this +section might be out of date and no longer applicable. Please try and use the +pytest runner if possible and report issues and missing features. + +Running an Individual Test +--- +The tests can be run individually using the following command: + + sh legacy.run.sh [flags] [] + +e.g. + + sh legacy.run.sh [flags] notify + +Optional flags are: + + -k Keep servers running after the test completes. Each test + usually starts a number of nameservers, either instances + of the "named" being tested, or custom servers (written in + Python or Perl) that feature test-specific behavior. The + servers are automatically started before the test is run + and stopped after it ends. This flag leaves them running + at the end of the test, so that additional queries can be + sent by hand. To stop the servers afterwards, use the + command "sh stop.sh ". + + -n Noclean - do not remove the output files if the test + completes successfully. By default, files created by the + test are deleted if it passes; they are not deleted if the + test fails. + + -p Sets the range of ports used by the test. A block of 100 + ports is available for each test, the number given to the + "-p" switch being the number of the start of that block + (e.g. "-p 7900" will mean that the test is able to use + ports 7900 through 7999). If not specified, the test will + have ports 5000 to 5099 available to it. + +Arguments are: + + test-name Mandatory. The name of the test, which is the name of the + subdirectory in bin/tests/system holding the test files. + + test-arguments Optional arguments that are passed to each of the test's + scripts. + + +Running All The System Tests +--- +To run all the system tests, enter the command: + + sh runall.sh [-c] [-n] [numproc] + +The optional flag "-c" forces colored output (by default system test output is +not printed in color due to legacy.run.sh being piped through "tee"). + +The optional flag "-n" has the same effect as it does for "legacy.run.sh" - it +causes the retention of all output files from all tests. + +The optional "numproc" argument specifies the maximum number of tests that can +run in parallel. The default is 1, which means that all of the tests run +sequentially. If greater than 1, up to "numproc" tests will run simultaneously, +new tests being started as tests finish. Each test will get a unique set of +ports, so there is no danger of tests interfering with one another. Parallel +running will reduce the total time taken to run the BIND system tests, but will +mean that the output from all the tests sent to the screen will be mixed up +with one another. However, the systests.output file produced at the end of the +run (in the bin/tests/system directory) will contain the output from each test +in sequential order. + +Note that it is not possible to pass arguments to tests though the "runall.sh" +script. + +A run of all the system tests can also be initiated via make: + + make [-j numproc] test + +In this case, retention of the output files after a test completes successfully +is specified by setting the environment variable SYSTEMTEST_NO_CLEAN to 1 prior +to running make, e.g. + + SYSTEMTEST_NO_CLEAN=1 make [-j numproc] test + +while setting environment variable SYSTEMTEST_FORCE_COLOR to 1 forces system +test output to be printed in color. + + +Running Multiple System Test Suites Simultaneously +--- +In some cases it may be desirable to have multiple instances of the system test +suite running simultaneously (e.g. from different terminal windows). To do +this: + +1. Each installation must have its own directory tree. The system tests create +files in the test directories, so separate directory trees are required to +avoid interference between the same test running in the different +installations. + +2. For one of the test suites, the starting port number must be specified by +setting the environment variable STARTPORT before starting the test suite. +Each test suite comprises about 100 tests, each being allocated a set of 100 +ports. The port ranges for each test are allocated sequentially, so each test +suite requires about 10,000 ports to itself. By default, the port allocation +starts at 5,000. So the following set of commands: + + Terminal Window 1: + cd /bin/tests/system + sh runall.sh 4 + + Terminal Window 2: + cd /bin/tests/system + STARTPORT=20000 sh runall.sh 4 + +... will start the test suite for installation-1 using the default base port +of 5,000, so the test suite will use ports 5,000 through 15,000 (or there +abouts). The use of "STARTPORT=20000" to prefix the run of the test suite for +installation-2 will mean the test suite uses ports 20,000 through 30,000 or so. + + +Format of Test Output +--- +All output from the system tests is in the form of lines with the following +structure: + + :: [()] + +e.g. + + I:catz:checking that dom1.example is not served by primary (1) + +The meanings of the fields are as follows: + + +This indicates the type of message. This is one of: + + S Start of the test + A Start of test (retained for backwards compatibility) + T Start of test (retained for backwards compatibility) + E End of the test + I Information. A test will typically output many of these messages + during its run, indicating test progress. Note that such a message may + be of the form "I:testname:failed", indicating that a sub-test has + failed. + R Result. Each test will result in one such message, which is of the + form: + + R:: + + where is one of: + + PASS The test passed + FAIL The test failed + SKIPPED The test was not run, usually because some + prerequisites required to run the test are missing. + + +This is the name of the test from which the message emanated, which is also the +name of the subdirectory holding the test files. + + +This is text output by the test during its execution. + +() +If present, this will correlate with a file created by the test. The tests +execute commands and route the output of each command to a file. The name of +this file depends on the command and the test, but will usually be of the form: + + .out. + +e.g. nsupdate.out.test28, dig.out.q3. This aids diagnosis of problems by +allowing the output that caused the problem message to be identified. + + +Re-Running the Tests +--- +If there is a requirement to re-run a test (or the entire test suite), the +files produced by the tests should be deleted first. Normally, these files are +deleted if the test succeeds but are retained on error. The legacy.run.sh +script automatically calls a given test's clean.sh script before invoking its +setup.sh script. + +Deletion of the files produced by the set of tests (e.g. after the execution +of "runall.sh") can be carried out using the command: + + sh cleanall.sh + +or + + make testclean + +(Note that the Makefile has two other targets for cleaning up files: "clean" +will delete all the files produced by the tests, as well as the object and +executable files used by the tests. "distclean" does all the work of "clean" +as well as deleting configuration files produced by "configure".) + + +Developer Notes +=== +This section is intended for developers writing new tests. + + +Overview +--- +As noted above, each test is in a separate directory. To interact with the +test framework, the directories contain the following standard files: + +prereq.sh Run at the beginning to determine whether the test can be run at + all; if not, we see a R:SKIPPED result. This file is optional: + if not present, the test is assumed to have all its prerequisites + met. + +setup.sh Run after prereq.sh, this sets up the preconditions for the tests. + Although optional, virtually all tests will require such a file to + set up the ports they should use for the test. + +tests.sh Runs the actual tests. This file is mandatory. + +clean.sh Run at the end to clean up temporary files, but only if the test + was completed successfully and its running was not inhibited by the + "-n" switch being passed to "legacy.run.sh". Otherwise the + temporary files are left in place for inspection. + +ns These subdirectories contain test name servers that can be queried + or can interact with each other. The value of N indicates the + address the server listens on: for example, ns2 listens on + 10.53.0.2, and ns4 on 10.53.0.4. All test servers use an + unprivileged port, so they don't need to run as root. These + servers log at the highest debug level and the log is captured in + the file "named.run". + +ans Like ns[X], but these are simple mock name servers implemented in + Perl or Python. They are generally programmed to misbehave in ways + named would not so as to exercise named's ability to interoperate + with badly behaved name servers. + + +Port Usage +--- +In order for the tests to run in parallel, each test requires a unique set of +ports. These are specified by the "-p" option passed to "legacy.run.sh", which +sets environment variables that the scripts listed above can reference. + +The convention used in the system tests is that the number passed is the start +of a range of 100 ports. The test is free to use the ports as required, +although the first ten ports in the block are named and generally tests use the +named ports for their intended purpose. The names of the environment variables +are: + + PORT Number to be used for the query port. + CONTROLPORT Number to be used as the RNDC control port. + EXTRAPORT1 - EXTRAPORT8 Eight port numbers that can be used as needed. + +Two other environment variables are defined: + + LOWPORT The lowest port number in the range. + HIGHPORT The highest port number in the range. + +Since port ranges usually start on a boundary of 10, the variables are set such +that the last digit of the port number corresponds to the number of the +EXTRAPORTn variable. For example, if the port range were to start at 5200, the +port assignments would be: + + PORT = 5200 + EXTRAPORT1 = 5201 + : + EXTRAPORT8 = 5208 + CONTROLPORT = 5209 + LOWPORT = 5200 + HIGHPORT = 5299 + +When running tests in parallel (i.e. giving a value of "numproc" greater than 1 +in the "make" or "runall.sh" commands listed above), it is guaranteed that each +test will get a set of unique port numbers. + + +Writing a Test +--- +The test framework requires up to four shell scripts (listed above) as well as +a number of nameserver instances to run. Certain expectations are put on each +script: + + +General +--- +1. Each of the four scripts will be invoked with the command + + (cd ; sh