diff --git a/CHANGES b/CHANGES index 951e3b68cc..031208f2ff 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +5696. [protocol] Add support for HTTPS and SVCB record types. [GL #1132] + 5695. [func] Dig can now display the BADCOOKIE message as part of processing it (+showbadcookie). [GL #2319] diff --git a/bin/tests/system/checkzone/tests.sh b/bin/tests/system/checkzone/tests.sh index 8bab669a50..c9614a4b65 100644 --- a/bin/tests/system/checkzone/tests.sh +++ b/bin/tests/system/checkzone/tests.sh @@ -37,7 +37,7 @@ do echo_i "checking $db ($n)" ret=0 v=0 case $db in - zones/bad-dns-sd-reverse.db) + zones/bad-dns-sd-reverse.db|zones/bad-svcb-servername.db) $CHECKZONE -k fail -i local 0.0.0.0.in-addr.arpa $db > test.out.$n 2>&1 || v=$? ;; *) diff --git a/bin/tests/system/checkzone/zones/bad-svcb-mandatory.db b/bin/tests/system/checkzone/zones/bad-svcb-mandatory.db new file mode 100644 index 0000000000..d7048798e9 --- /dev/null +++ b/bin/tests/system/checkzone/zones/bad-svcb-mandatory.db @@ -0,0 +1,15 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; 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 http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 600 +@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 + NS ns +ns A 192.0.2.1 + +svcb SVCB 0 . mandatory=alpn diff --git a/bin/tests/system/checkzone/zones/bad-svcb-servername.db b/bin/tests/system/checkzone/zones/bad-svcb-servername.db new file mode 100644 index 0000000000..91fc2dfe32 --- /dev/null +++ b/bin/tests/system/checkzone/zones/bad-svcb-servername.db @@ -0,0 +1,15 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; 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 http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 600 +@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 + NS ns +ns A 192.0.2.1 + +svcb SVCB 1 _underscore.example. port=60 alpn=h3 ech="ZWFzdGVyIGVnZyE=" diff --git a/bin/tests/system/checkzone/zones/bad-svcb.db b/bin/tests/system/checkzone/zones/bad-svcb.db new file mode 100644 index 0000000000..d40e8db980 --- /dev/null +++ b/bin/tests/system/checkzone/zones/bad-svcb.db @@ -0,0 +1,15 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; 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 http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 600 +@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 + NS ns +ns A 192.0.2.1 + +svcb SVCB 0 . unknown=wha diff --git a/bin/tests/system/checkzone/zones/good-svcb.db b/bin/tests/system/checkzone/zones/good-svcb.db new file mode 100644 index 0000000000..8b1e4f8a01 --- /dev/null +++ b/bin/tests/system/checkzone/zones/good-svcb.db @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; 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 http://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 600 +@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 + NS ns +ns A 192.0.2.1 + +svcb0 SVCB 0 example.net. +svcb1 SVCB 1 . port=60 alpn=h3 ech="ZWFzdGVyIGVnZyE=" +svcb2 SVCB 2 . no-default-alpn alpn=alpn +svcb3 SVCB 3 . ipv4hint="10.10.10.10" +svcb4 SVCB 4 . ipv6hint="feed:a::bee" +svcb5 SVCB 5 . key9999="something" +svcb6 SVCB 6 . mandatory=port,alpn port=60 alpn=h3 +svcb7 SVCB 7 . mandatory=port,alpn port=60 alpn=h1,h3 +svcb8 SVCB 8 . mandatory=port,alpn port=60 alpn="h1\\,h2,h3" +svcb9 SVCB 0 44._svbc.example.net. diff --git a/bin/tests/system/doth/example.axfr.good b/bin/tests/system/doth/example.axfr.good index 01e15fa9a2..653fb46bda 100644 --- a/bin/tests/system/doth/example.axfr.good +++ b/bin/tests/system/doth/example.axfr.good @@ -2558,6 +2558,8 @@ hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" hinfo02.example. 3600 IN HINFO "PC" "NetBSD" hip1.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip2.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. +https0.example. 3600 IN HTTPS 0 example.net. +https1.example. 3600 IN HTTPS 1 . port=60 ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey03.example. 3600 IN IPSECKEY 10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== @@ -2634,6 +2636,8 @@ srv01.example. 3600 IN SRV 0 0 0 . srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example. sshfp01.example. 3600 IN SSHFP 4 2 C76D8329954DA2835751E371544E963EFDA099080D6C58DD2BFD9A31 6E162C83 sshfp02.example. 3600 IN SSHFP 1 2 BF29468C83AC58CCF8C85AB7B3BEB054ECF1E38512B8353AB36471FA 88961DCC +svcb0.example. 3600 IN SVCB 0 example.net. +svcb1.example. 3600 IN SVCB 1 . port=60 ta.example. 3600 IN TA 30795 1 1 310D27F4D82C1FC2400704EA9939FE6E1CEAA3B9 talink0.example. 3600 IN TALINK . talink1.example. talink1.example. 3600 IN TALINK talink0.example. talink2.example. diff --git a/bin/tests/system/genzone.sh b/bin/tests/system/genzone.sh index 2f8882b401..88ae7f04e9 100644 --- a/bin/tests/system/genzone.sh +++ b/bin/tests/system/genzone.sh @@ -493,6 +493,12 @@ dlv DLV 30795 1 1 ( ; type 65280-65534 (private use) +https0 HTTPS 0 example.net. +https1 HTTPS 1 . port=60 + +svcb0 SVCB 0 example.net. +svcb1 SVCB 1 . port=60 + ; keydata (internal type used for managed keys) keydata TYPE65533 \# 0 keydata TYPE65533 \# 6 010203040506 diff --git a/bin/tests/system/resolver/ns6/example.net.db.in b/bin/tests/system/resolver/ns6/example.net.db.in index eab3267b79..e3699c1889 100644 --- a/bin/tests/system/resolver/ns6/example.net.db.in +++ b/bin/tests/system/resolver/ns6/example.net.db.in @@ -13,6 +13,17 @@ $TTL 600 @ IN MX 0 mail ns IN A 10.53.0.6 mail IN A 10.53.0.6 +www IN HTTPS 0 http-server +http-server IN A 10.53.0.6 +https-loop IN HTTPS 0 https-next +https-loop IN A 10.53.0.6 +https-next IN HTTPS 0 https-loop +https-next IN A 10.53.0.7 +https-cname IN HTTPS 0 cname-server +cname-server IN CNAME cname-next +cname-next IN CNAME http-server +https-cname-loop IN HTTPS 0 https-cname-loop0 +https-cname-loop0 IN CNAME https-cname-loop0 fetch 10 IN TXT A short ttl non-zero 10 IN TXT A short ttl zero 0 IN TXT A zero ttl diff --git a/bin/tests/system/resolver/ns6/named.conf.in b/bin/tests/system/resolver/ns6/named.conf.in index 1726d5fa5b..5c631ae111 100644 --- a/bin/tests/system/resolver/ns6/named.conf.in +++ b/bin/tests/system/resolver/ns6/named.conf.in @@ -28,6 +28,7 @@ options { * has a exclude list. */ root-delegation-only exclude { "a"; }; + max-udp-size 4096; }; zone "." { diff --git a/bin/tests/system/resolver/ns7/named1.conf.in b/bin/tests/system/resolver/ns7/named1.conf.in index 47ce301e86..ec4ade0c51 100644 --- a/bin/tests/system/resolver/ns7/named1.conf.in +++ b/bin/tests/system/resolver/ns7/named1.conf.in @@ -29,6 +29,7 @@ options { */ prefetch 0; querylog yes; + edns-udp-size 4096; }; key rndc_key { diff --git a/bin/tests/system/resolver/ns7/named2.conf.in b/bin/tests/system/resolver/ns7/named2.conf.in index 47ce301e86..ec4ade0c51 100644 --- a/bin/tests/system/resolver/ns7/named2.conf.in +++ b/bin/tests/system/resolver/ns7/named2.conf.in @@ -29,6 +29,7 @@ options { */ prefetch 0; querylog yes; + edns-udp-size 4096; }; key rndc_key { diff --git a/bin/tests/system/resolver/tests.sh b/bin/tests/system/resolver/tests.sh index 435501c2a4..165b196b70 100755 --- a/bin/tests/system/resolver/tests.sh +++ b/bin/tests/system/resolver/tests.sh @@ -845,5 +845,49 @@ grep "status: NXDOMAIN" dig.ns1.out.${n} > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=`expr $status + $ret` +n=`expr $n + 1` +echo_i "check that the addition section for HTTPS is populated on initial query to a recursive server ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.7 www.example.net https > dig.out.ns7.${n} || ret=1 +grep "status: NOERROR" dig.out.ns7.${n} > /dev/null || ret=1 +grep "flags:[^;]* ra[ ;]" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ADDITIONAL: 2" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns7.${n} > /dev/null || ret=1 +grep "http-server\.example\.net\..*A.*10\.53\.0\.6" dig.out.ns7.${n} > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check HTTPS loop is handled properly ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.7 https-loop.example.net https > dig.out.ns7.${n} || ret=1 +grep "status: NOERROR" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns7.${n} > /dev/null || ret=1 +grep "ADDITIONAL: 2" dig.out.ns7.${n} > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check HTTPS -> CNAME loop is handled properly ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.7 https-cname-loop.example.net https > dig.out.ns7.${n} || ret=1 +grep "status: NOERROR" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ADDITIONAL: 2" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ANSWER: 1," dig.out.ns7.${n} > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + +n=`expr $n + 1` +echo_i "check HTTPS cname chains are followed ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.7 https-cname.example.net https > dig.out.ns7.${n} || ret=1 +grep "status: NOERROR" dig.out.ns7.${n} > /dev/null || ret=1 +grep "ADDITIONAL: 4" dig.out.ns7.${n} > /dev/null || ret=1 +grep 'http-server\.example\.net\..*A.10\.53\.0\.6' dig.out.ns7.${n} > /dev/null || ret=1 +grep 'cname-server\.example\.net\..*CNAME.cname-next\.example\.net\.' dig.out.ns7.${n} > /dev/null || ret=1 +grep 'cname-next\.example\.net\..*CNAME.http-server\.example\.net\.' dig.out.ns7.${n} > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=`expr $status + $ret` + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/rrchecker/typelist.good b/bin/tests/system/rrchecker/typelist.good index c843fc7b51..3c3e5cd384 100644 --- a/bin/tests/system/rrchecker/typelist.good +++ b/bin/tests/system/rrchecker/typelist.good @@ -59,6 +59,8 @@ CDNSKEY OPENPGPKEY CSYNC ZONEMD +SVCB +HTTPS SPF UINFO UID diff --git a/bin/tests/system/xfer/dig1.good b/bin/tests/system/xfer/dig1.good index 0f8fad0f31..9fa5437d38 100644 --- a/bin/tests/system/xfer/dig1.good +++ b/bin/tests/system/xfer/dig1.good @@ -59,6 +59,8 @@ hinfo01.example. 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" hinfo02.example. 3600 IN HINFO "PC" "NetBSD" hip1.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip2.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. +https0.example. 3600 IN HTTPS 0 example.net. +https1.example. 3600 IN HTTPS 1 . port=60 ipseckey01.example. 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey02.example. 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ipseckey03.example. 3600 IN IPSECKEY 10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== @@ -137,6 +139,8 @@ sink02.example. 3600 IN SINK 8 0 2 l4ik smimea.example. 3600 IN SMIMEA 1 1 2 92003BA34942DC74152E2F2C408D29ECA5A520E7F2E06BB944F4DCA3 46BAF63C1B177615D466F6C4B71C216A50292BD58C9EBDD2F74E38FE 51FFD48C43326CBC srv01.example. 3600 IN SRV 0 0 0 . srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example. +svcb0.example. 3600 IN SVCB 0 example.net. +svcb1.example. 3600 IN SVCB 1 . port=60 ta.example. 3600 IN TA 30795 1 1 310D27F4D82C1FC2400704EA9939FE6E1CEAA3B9 talink0.example. 3600 IN TALINK . talink1.example. talink1.example. 3600 IN TALINK talink0.example. talink2.example. diff --git a/bin/tests/system/xfer/dig2.good b/bin/tests/system/xfer/dig2.good index fed8926817..2229f9ce7a 100644 --- a/bin/tests/system/xfer/dig2.good +++ b/bin/tests/system/xfer/dig2.good @@ -69,6 +69,8 @@ isdn04.example. 3600 IN ISDN "isdn-address" "subaddress" hip1.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D hip2.example. 3600 IN HIP 2 200100107B1A74DF365639CC39F1D578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. dnskey01.example. 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRuniJDBzC7w0aRyzWZriO6i2od GWWQVucZqKVsENW91IOW4vqudngPZsY3GvQ/xVA8/7pyFj6b7Esga60z yGW6LFe9r8n6paHrlG5ojqf0BaqHT+8= +https0.example. 3600 IN HTTPS 0 example.net. +https1.example. 3600 IN HTTPS 1 . port=60 keydata.example. 3600 IN TYPE65533 \# 0 keydata.example. 3600 IN TYPE65533 \# 6 010203040506 keydata.example. 3600 IN TYPE65533 \# 18 010203040506010203040506010203040506 @@ -137,6 +139,8 @@ srv01.example. 3600 IN SRV 0 0 0 . srv02.example. 3600 IN SRV 65535 65535 65535 old-slow-box.example. sshfp01.example. 3600 IN SSHFP 4 2 C76D8329954DA2835751E371544E963EFDA099080D6C58DD2BFD9A31 6E162C83 sshfp02.example. 3600 IN SSHFP 1 2 BF29468C83AC58CCF8C85AB7B3BEB054ECF1E38512B8353AB36471FA 88961DCC +svcb0.example. 3600 IN SVCB 0 example.net. +svcb1.example. 3600 IN SVCB 1 . port=60 ta.example. 3600 IN TA 30795 1 1 310D27F4D82C1FC2400704EA9939FE6E1CEAA3B9 talink0.example. 3600 IN TALINK . talink1.example. talink1.example. 3600 IN TALINK talink0.example. talink2.example. diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index b6cee02efe..e17d4ebb8c 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -35,6 +35,8 @@ New Features parameters to an ``http`` statement. The defaults are 300 and 100 respectively. :gl:`#2809` +- Add support for HTTPS and SVCB record types. :gl:`#1132` + Removed Features ~~~~~~~~~~~~~~~~ diff --git a/fuzz/dns_rdata_fromwire_text.in/svcb b/fuzz/dns_rdata_fromwire_text.in/svcb new file mode 100644 index 0000000000..800a616701 Binary files /dev/null and b/fuzz/dns_rdata_fromwire_text.in/svcb differ diff --git a/lib/dns/gen.c b/lib/dns/gen.c index 2939c175e7..09b877e6ff 100644 --- a/lib/dns/gen.c +++ b/lib/dns/gen.c @@ -78,7 +78,7 @@ #define COMPARETYPE "rdata1->type" #define COMPAREDEF "use_default = true" -#define ADDITIONALDATAARGS "rdata, add, arg" +#define ADDITIONALDATAARGS "rdata, owner, add, arg" #define ADDITIONALDATACLASS "rdata->rdclass" #define ADDITIONALDATATYPE "rdata->type" #define ADDITIONALDATADEF "use_default = true" diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 359f69d674..245b69b29c 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -594,8 +594,8 @@ dns_rdatatype_isknown(dns_rdatatype_t type); */ isc_result_t -dns_rdata_additionaldata(dns_rdata_t *rdata, dns_additionaldatafunc_t add, - void *arg); +dns_rdata_additionaldata(dns_rdata_t *rdata, const dns_name_t *owner, + dns_additionaldatafunc_t add, void *arg); /*%< * Call 'add' for each name and type from 'rdata' which is subject to * additional section processing. @@ -695,6 +695,21 @@ dns_rdatatype_atcname(dns_rdatatype_t type); * */ +bool +dns_rdatatype_followadditional(dns_rdatatype_t type); +/*%< + * Return true if adding a record of type 'type' to the ADDITIONAL section + * of a message can itself trigger the addition of still more data to the + * additional section. + * + * (For example: adding SRV to the ADDITIONAL section may trigger + * the addition of address records associated with that SRV.) + * + * Requires: + * \li 'type' is a valid rdata type. + * + */ + unsigned int dns_rdatatype_attributes(dns_rdatatype_t rdtype); /*%< @@ -729,6 +744,8 @@ dns_rdatatype_attributes(dns_rdatatype_t rdtype); #define DNS_RDATATYPEATTR_ATPARENT 0x00000200U /*% Can exist along side a CNAME */ #define DNS_RDATATYPEATTR_ATCNAME 0x00000400U +/*% Follow additional */ +#define DNS_RDATATYPEATTR_FOLLOWADDITIONAL 0x00000800U dns_rdatatype_t dns_rdata_covers(dns_rdata_t *rdata); diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index 2569e40d23..f5a4f2be35 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -439,6 +439,7 @@ dns_rdataset_towirepartial(dns_rdataset_t * rdataset, isc_result_t dns_rdataset_additionaldata(dns_rdataset_t * rdataset, + const dns_name_t * owner_name, dns_additionaldatafunc_t add, void *arg); /*%< * For each rdata in rdataset, call 'add' for each name and type in the diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 58e7426b3f..22a75b5d24 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -162,8 +162,9 @@ #define DNS_R_NSEC3SALTRANGE (ISC_RESULTCLASS_DNS + 124) #define DNS_R_NSEC3BADALG (ISC_RESULTCLASS_DNS + 125) #define DNS_R_NSEC3RESALT (ISC_RESULTCLASS_DNS + 126) +#define DNS_R_INCONSISTENTRR (ISC_RESULTCLASS_DNS + 127) -#define DNS_R_NRESULTS 127 /*%< Number of results */ +#define DNS_R_NRESULTS 128 /*%< Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 21ced9e4e6..f42bd80cd7 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -396,7 +396,8 @@ typedef isc_result_t (*dns_addrdatasetfunc_t)(void *, const dns_name_t *, dns_rdataset_t *); typedef isc_result_t (*dns_additionaldatafunc_t)(void *, const dns_name_t *, - dns_rdatatype_t); + dns_rdatatype_t, + dns_rdataset_t *); typedef isc_result_t (*dns_digestfunc_t)(void *, isc_region_t *); diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 32355c1398..3b5035ea34 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -10084,7 +10084,8 @@ maybe_rehash_gluetable(rbtdb_version_t *version) { } static isc_result_t -glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { +glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, + dns_rdataset_t *unused) { rbtdb_glue_additionaldata_ctx_t *ctx; isc_result_t result; dns_fixedname_t fixedname_a; @@ -10098,6 +10099,8 @@ glue_nsdname_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { rbtdb_glue_t *glue = NULL; dns_name_t *gluename = NULL; + UNUSED(unused); + /* * NS records want addresses in additional records. */ @@ -10397,7 +10400,8 @@ no_glue: maybe_rehash_gluetable(rbtversion); idx = hash_32(hash, rbtversion->glue_table_bits); - (void)dns_rdataset_additionaldata(rdataset, glue_nsdname_cb, &ctx); + (void)dns_rdataset_additionaldata(rdataset, dns_rootname, + glue_nsdname_cb, &ctx); cur = isc_mem_get(rbtdb->common.mctx, sizeof(*cur)); diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 6fb7bc3d22..a655233204 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -29,12 +29,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -118,10 +120,11 @@ #define CALL_FREESTRUCT source -#define ARGS_ADDLDATA \ - dns_rdata_t *rdata, dns_additionaldatafunc_t add, void *arg +#define ARGS_ADDLDATA \ + dns_rdata_t *rdata, const dns_name_t *owner, \ + dns_additionaldatafunc_t add, void *arg -#define CALL_ADDLDATA rdata, add, arg +#define CALL_ADDLDATA rdata, owner, add, arg #define ARGS_DIGEST dns_rdata_t *rdata, dns_digestfunc_t digest, void *arg @@ -159,6 +162,13 @@ txt_fromtext(isc_textregion_t *source, isc_buffer_t *target); static isc_result_t txt_fromwire(isc_buffer_t *source, isc_buffer_t *target); +static isc_result_t +commatxt_fromtext(isc_textregion_t *source, bool comma, isc_buffer_t *target); + +static isc_result_t +commatxt_totext(isc_region_t *source, bool quote, bool comma, + isc_buffer_t *target); + static isc_result_t multitxt_totext(isc_region_t *source, isc_buffer_t *target); @@ -302,6 +312,22 @@ static isc_result_t generic_tostruct_tlsa(ARGS_TOSTRUCT); static void generic_freestruct_tlsa(ARGS_FREESTRUCT); +static isc_result_t generic_fromtext_in_svcb(ARGS_FROMTEXT); +static isc_result_t generic_totext_in_svcb(ARGS_TOTEXT); +static isc_result_t generic_fromwire_in_svcb(ARGS_FROMWIRE); +static isc_result_t generic_towire_in_svcb(ARGS_TOWIRE); +static isc_result_t generic_fromstruct_in_svcb(ARGS_FROMSTRUCT); +static isc_result_t generic_tostruct_in_svcb(ARGS_TOSTRUCT); +static void generic_freestruct_in_svcb(ARGS_FREESTRUCT); +static isc_result_t generic_additionaldata_in_svcb(ARGS_ADDLDATA); +static bool generic_checknames_in_svcb(ARGS_CHECKNAMES); +static isc_result_t +generic_rdata_in_svcb_first(dns_rdata_in_svcb_t *); +static isc_result_t +generic_rdata_in_svcb_next(dns_rdata_in_svcb_t *); +static void +generic_rdata_in_svcb_current(dns_rdata_in_svcb_t *, isc_region_t *); + /*% INT16 Size */ #define NS_INT16SZ 2 /*% IPv6 Address Size */ @@ -1242,8 +1268,8 @@ dns_rdata_freestruct(void *source) { } isc_result_t -dns_rdata_additionaldata(dns_rdata_t *rdata, dns_additionaldatafunc_t add, - void *arg) { +dns_rdata_additionaldata(dns_rdata_t *rdata, const dns_name_t *owner, + dns_additionaldatafunc_t add, void *arg) { isc_result_t result = ISC_R_NOTIMPLEMENTED; bool use_default = false; @@ -1415,7 +1441,8 @@ name_length(const dns_name_t *name) { } static isc_result_t -txt_totext(isc_region_t *source, bool quote, isc_buffer_t *target) { +commatxt_totext(isc_region_t *source, bool quote, bool comma, + isc_buffer_t *target) { unsigned int tl; unsigned int n; unsigned char *sp; @@ -1445,30 +1472,48 @@ txt_totext(isc_region_t *source, bool quote, isc_buffer_t *target) { /* * \DDD space (0x20) if not quoting. */ - if (*sp < (quote ? 0x20 : 0x21) || *sp >= 0x7f) { + if (*sp < (quote ? ' ' : '!') || *sp >= 0x7f) { if (tl < 4) { return (ISC_R_NOSPACE); } - *tp++ = 0x5c; - *tp++ = 0x30 + ((*sp / 100) % 10); - *tp++ = 0x30 + ((*sp / 10) % 10); - *tp++ = 0x30 + (*sp % 10); + *tp++ = '\\'; + *tp++ = '0' + ((*sp / 100) % 10); + *tp++ = '0' + ((*sp / 10) % 10); + *tp++ = '0' + (*sp % 10); sp++; tl -= 4; continue; } /* * Escape double quote and backslash. If we are not - * enclosing the string in double quotes also escape - * at sign and semicolon. + * enclosing the string in double quotes, also escape + * at sign (@) and semicolon (;) unless comma is set. + * If comma is set, then only escape commas (,). */ - if (*sp == 0x22 || *sp == 0x5c || - (!quote && (*sp == 0x40 || *sp == 0x3b))) { + if (*sp == '"' || *sp == '\\' || (comma && *sp == ',') || + (!comma && !quote && (*sp == '@' || *sp == ';'))) + { if (tl < 2) { return (ISC_R_NOSPACE); } *tp++ = '\\'; tl--; + /* + * Perform comma escape processing. + * ',' => '\\,' + * '\' => '\\\\' + */ + if (comma && (*sp == ',' || *sp == '\\')) { + if (tl < ((*sp == '\\') ? 3 : 2)) { + return (ISC_R_NOSPACE); + } + *tp++ = '\\'; + tl--; + if (*sp == '\\') { + *tp++ = '\\'; + tl--; + } + } } if (tl < 1) { return (ISC_R_NOSPACE); @@ -1490,9 +1535,14 @@ txt_totext(isc_region_t *source, bool quote, isc_buffer_t *target) { } static isc_result_t -txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { +txt_totext(isc_region_t *source, bool quote, isc_buffer_t *target) { + return (commatxt_totext(source, quote, false, target)); +} + +static isc_result_t +commatxt_fromtext(isc_textregion_t *source, bool comma, isc_buffer_t *target) { isc_region_t tregion; - bool escape; + bool escape = false, comma_escape = false, seen_comma = false; unsigned int n, nrem; char *s; unsigned char *t; @@ -1504,7 +1554,6 @@ txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { n = source->length; t = tregion.base; nrem = tregion.length; - escape = false; if (nrem < 1) { return (ISC_R_NOSPACE); } @@ -1549,6 +1598,25 @@ txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { continue; } escape = false; + /* + * Level 1 escape processing complete. + * If comma is set perform comma escape processing. + * + * Level 1 Level 2 ALPN's + * h1\,h2 => h1,h2 => h1 and h2 + * h1\\,h2 => h1\,h2 => h1,h2 + * h1\\h2 => h1\h2 => h1h2 + * h1\\\\h2 => h1\\h2 => h1\h2 + */ + if (comma && !comma_escape && c == ',') { + seen_comma = true; + break; + } + if (comma && !comma_escape && c == '\\') { + comma_escape = true; + continue; + } + comma_escape = false; if (nrem == 0) { return ((tregion.length <= 256U) ? ISC_R_NOSPACE : DNS_R_SYNTAX); @@ -1556,14 +1624,41 @@ txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { *t++ = c; nrem--; } - if (escape) { + + /* + * Incomplete escape processing? + */ + if (escape || (comma && comma_escape)) { return (DNS_R_SYNTAX); } + + if (comma) { + /* + * Disallow empty ALPN at start (",h1") or in the + * middle ("h1,,h2"). + */ + if (s == source->base || (seen_comma && s == source->base + 1)) + { + return (DNS_R_SYNTAX); + } + isc_textregion_consume(source, s - source->base); + /* + * Disallow empty ALPN at end ("h1,"). + */ + if (seen_comma && source->length == 0) { + return (DNS_R_SYNTAX); + } + } *tregion.base = (unsigned char)(t - tregion.base - 1); isc_buffer_add(target, *tregion.base + 1); return (ISC_R_SUCCESS); } +static isc_result_t +txt_fromtext(isc_textregion_t *source, isc_buffer_t *target) { + return (commatxt_fromtext(source, false, target)); +} + static isc_result_t txt_fromwire(isc_buffer_t *source, isc_buffer_t *target) { unsigned int n; @@ -1618,20 +1713,20 @@ multitxt_totext(isc_region_t *source, isc_buffer_t *target) { n0 = source->length - 1; while (n--) { - if (*sp < 0x20 || *sp >= 0x7f) { + if (*sp < ' ' || *sp >= 0x7f) { if (tl < 4) { return (ISC_R_NOSPACE); } - *tp++ = 0x5c; - *tp++ = 0x30 + ((*sp / 100) % 10); - *tp++ = 0x30 + ((*sp / 10) % 10); - *tp++ = 0x30 + (*sp % 10); + *tp++ = '\\'; + *tp++ = '0' + ((*sp / 100) % 10); + *tp++ = '0' + ((*sp / 10) % 10); + *tp++ = '0' + (*sp % 10); sp++; tl -= 4; continue; } /* double quote, backslash */ - if (*sp == 0x22 || *sp == 0x5c) { + if (*sp == '"' || *sp == '\\') { if (tl < 2) { return (ISC_R_NOSPACE); } @@ -2131,6 +2226,15 @@ dns_rdatatype_atparent(dns_rdatatype_t type) { return (false); } +bool +dns_rdatatype_followadditional(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & + DNS_RDATATYPEATTR_FOLLOWADDITIONAL) != 0) { + return (true); + } + return (false); +} + bool dns_rdataclass_ismeta(dns_rdataclass_t rdclass) { if (rdclass == dns_rdataclass_reserved0 || diff --git a/lib/dns/rdata/any_255/tsig_250.c b/lib/dns/rdata/any_255/tsig_250.c index 0fb1211d13..083bde3345 100644 --- a/lib/dns/rdata/any_255/tsig_250.c +++ b/lib/dns/rdata/any_255/tsig_250.c @@ -567,6 +567,7 @@ additionaldata_any_tsig(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_any); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/ch_3/a_1.c b/lib/dns/rdata/ch_3/a_1.c index 23163a0e50..0df83160cb 100644 --- a/lib/dns/rdata/ch_3/a_1.c +++ b/lib/dns/rdata/ch_3/a_1.c @@ -261,6 +261,7 @@ additionaldata_ch_a(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_ch); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/afsdb_18.c b/lib/dns/rdata/generic/afsdb_18.c index 7fe396c7c5..d2b84b7273 100644 --- a/lib/dns/rdata/generic/afsdb_18.c +++ b/lib/dns/rdata/generic/afsdb_18.c @@ -247,12 +247,14 @@ additionaldata_afsdb(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_afsdb); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 2); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/amtrelay_260.c b/lib/dns/rdata/generic/amtrelay_260.c index 4e200049de..1cd8d1dd92 100644 --- a/lib/dns/rdata/generic/amtrelay_260.c +++ b/lib/dns/rdata/generic/amtrelay_260.c @@ -395,6 +395,7 @@ additionaldata_amtrelay(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_amtrelay); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/avc_258.c b/lib/dns/rdata/generic/avc_258.c index c269f01e4f..5f3de214be 100644 --- a/lib/dns/rdata/generic/avc_258.c +++ b/lib/dns/rdata/generic/avc_258.c @@ -95,6 +95,7 @@ additionaldata_avc(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_avc); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/caa_257.c b/lib/dns/rdata/generic/caa_257.c index 60c95a5a81..b443aa7d13 100644 --- a/lib/dns/rdata/generic/caa_257.c +++ b/lib/dns/rdata/generic/caa_257.c @@ -572,6 +572,7 @@ additionaldata_caa(ARGS_ADDLDATA) { REQUIRE(rdata->length >= 3U); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/cdnskey_60.c b/lib/dns/rdata/generic/cdnskey_60.c index 2fb006a31a..9eea7d6b8c 100644 --- a/lib/dns/rdata/generic/cdnskey_60.c +++ b/lib/dns/rdata/generic/cdnskey_60.c @@ -108,6 +108,7 @@ additionaldata_cdnskey(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_cdnskey); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/cds_59.c b/lib/dns/rdata/generic/cds_59.c index 56a847be59..26cc70d427 100644 --- a/lib/dns/rdata/generic/cds_59.c +++ b/lib/dns/rdata/generic/cds_59.c @@ -116,6 +116,7 @@ additionaldata_cds(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_cds); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/cert_37.c b/lib/dns/rdata/generic/cert_37.c index 8a171f8a4f..34f5ef7c9f 100644 --- a/lib/dns/rdata/generic/cert_37.c +++ b/lib/dns/rdata/generic/cert_37.c @@ -235,6 +235,7 @@ additionaldata_cert(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_cert); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/cname_5.c b/lib/dns/rdata/generic/cname_5.c index 69c28902fc..aa45ad3713 100644 --- a/lib/dns/rdata/generic/cname_5.c +++ b/lib/dns/rdata/generic/cname_5.c @@ -175,6 +175,7 @@ freestruct_cname(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_cname(ARGS_ADDLDATA) { UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/csync_62.c b/lib/dns/rdata/generic/csync_62.c index 66c37a6f16..eb41bcb640 100644 --- a/lib/dns/rdata/generic/csync_62.c +++ b/lib/dns/rdata/generic/csync_62.c @@ -214,6 +214,7 @@ additionaldata_csync(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_csync); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/dlv_32769.c b/lib/dns/rdata/generic/dlv_32769.c index 3b996adb1b..21ae76df86 100644 --- a/lib/dns/rdata/generic/dlv_32769.c +++ b/lib/dns/rdata/generic/dlv_32769.c @@ -112,6 +112,7 @@ additionaldata_dlv(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_dlv); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/dname_39.c b/lib/dns/rdata/generic/dname_39.c index 468a6e8a60..6964249b2e 100644 --- a/lib/dns/rdata/generic/dname_39.c +++ b/lib/dns/rdata/generic/dname_39.c @@ -175,12 +175,13 @@ freestruct_dname(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_dname(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_dname); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_dname); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/dnskey_48.c b/lib/dns/rdata/generic/dnskey_48.c index 1aa3f532e4..ca557bf59d 100644 --- a/lib/dns/rdata/generic/dnskey_48.c +++ b/lib/dns/rdata/generic/dnskey_48.c @@ -109,6 +109,7 @@ additionaldata_dnskey(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_dnskey); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/doa_259.c b/lib/dns/rdata/generic/doa_259.c index c8a735b180..5de79296c0 100644 --- a/lib/dns/rdata/generic/doa_259.c +++ b/lib/dns/rdata/generic/doa_259.c @@ -308,12 +308,13 @@ freestruct_doa(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_doa(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_doa); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_doa); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/ds_43.c b/lib/dns/rdata/generic/ds_43.c index bbfd136a68..0b91301c31 100644 --- a/lib/dns/rdata/generic/ds_43.c +++ b/lib/dns/rdata/generic/ds_43.c @@ -335,6 +335,7 @@ additionaldata_ds(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ds); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/eui48_108.c b/lib/dns/rdata/generic/eui48_108.c index 7be59c2c69..1a9820bbee 100644 --- a/lib/dns/rdata/generic/eui48_108.c +++ b/lib/dns/rdata/generic/eui48_108.c @@ -159,6 +159,7 @@ additionaldata_eui48(ARGS_ADDLDATA) { REQUIRE(rdata->length == 6); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/eui64_109.c b/lib/dns/rdata/generic/eui64_109.c index 381b87bbb8..50220c5199 100644 --- a/lib/dns/rdata/generic/eui64_109.c +++ b/lib/dns/rdata/generic/eui64_109.c @@ -162,6 +162,7 @@ additionaldata_eui64(ARGS_ADDLDATA) { REQUIRE(rdata->length == 8); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/gpos_27.c b/lib/dns/rdata/generic/gpos_27.c index d719fcc5e1..8bdfbdaae1 100644 --- a/lib/dns/rdata/generic/gpos_27.c +++ b/lib/dns/rdata/generic/gpos_27.c @@ -205,6 +205,7 @@ additionaldata_gpos(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_gpos); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/hinfo_13.c b/lib/dns/rdata/generic/hinfo_13.c index 2d5ed3e338..2e8a032bde 100644 --- a/lib/dns/rdata/generic/hinfo_13.c +++ b/lib/dns/rdata/generic/hinfo_13.c @@ -169,9 +169,10 @@ static inline isc_result_t additionaldata_hinfo(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_hinfo); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - UNUSED(rdata); return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/hip_55.c b/lib/dns/rdata/generic/hip_55.c index 9ead94ed4b..77d774e37b 100644 --- a/lib/dns/rdata/generic/hip_55.c +++ b/lib/dns/rdata/generic/hip_55.c @@ -379,12 +379,13 @@ freestruct_hip(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_hip(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_hip); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_hip); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/ipseckey_45.c b/lib/dns/rdata/generic/ipseckey_45.c index d93f3e4ab9..5404039216 100644 --- a/lib/dns/rdata/generic/ipseckey_45.c +++ b/lib/dns/rdata/generic/ipseckey_45.c @@ -440,6 +440,7 @@ additionaldata_ipseckey(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ipseckey); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/isdn_20.c b/lib/dns/rdata/generic/isdn_20.c index 264c7a0724..3054590d7c 100644 --- a/lib/dns/rdata/generic/isdn_20.c +++ b/lib/dns/rdata/generic/isdn_20.c @@ -196,6 +196,7 @@ additionaldata_isdn(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_isdn); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/key_25.c b/lib/dns/rdata/generic/key_25.c index 5867beee35..1351bfdd23 100644 --- a/lib/dns/rdata/generic/key_25.c +++ b/lib/dns/rdata/generic/key_25.c @@ -415,6 +415,7 @@ additionaldata_key(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_key); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/keydata_65533.c b/lib/dns/rdata/generic/keydata_65533.c index 6c78c98a1f..ae8d438e2e 100644 --- a/lib/dns/rdata/generic/keydata_65533.c +++ b/lib/dns/rdata/generic/keydata_65533.c @@ -412,6 +412,7 @@ additionaldata_keydata(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_keydata); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/l32_105.c b/lib/dns/rdata/generic/l32_105.c index b887285eb5..b2f02df646 100644 --- a/lib/dns/rdata/generic/l32_105.c +++ b/lib/dns/rdata/generic/l32_105.c @@ -178,6 +178,7 @@ additionaldata_l32(ARGS_ADDLDATA) { REQUIRE(rdata->length == 6); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/l64_106.c b/lib/dns/rdata/generic/l64_106.c index c225b0f6ef..6ff8fa83bd 100644 --- a/lib/dns/rdata/generic/l64_106.c +++ b/lib/dns/rdata/generic/l64_106.c @@ -172,6 +172,7 @@ additionaldata_l64(ARGS_ADDLDATA) { REQUIRE(rdata->length == 10); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/loc_29.c b/lib/dns/rdata/generic/loc_29.c index 478723ae6c..8c8afc374c 100644 --- a/lib/dns/rdata/generic/loc_29.c +++ b/lib/dns/rdata/generic/loc_29.c @@ -785,6 +785,7 @@ additionaldata_loc(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_loc); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/lp_107.c b/lib/dns/rdata/generic/lp_107.c index ed4cd199e3..7164394aac 100644 --- a/lib/dns/rdata/generic/lp_107.c +++ b/lib/dns/rdata/generic/lp_107.c @@ -193,16 +193,18 @@ additionaldata_lp(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_lp); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 2); dns_name_fromregion(&name, ®ion); - result = (add)(arg, &name, dns_rdatatype_l32); + result = (add)(arg, &name, dns_rdatatype_l32, NULL); if (result != ISC_R_SUCCESS) { return (result); } - return ((add)(arg, &name, dns_rdatatype_l64)); + return ((add)(arg, &name, dns_rdatatype_l64, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/mb_7.c b/lib/dns/rdata/generic/mb_7.c index 9a037cc7ba..0702fbe490 100644 --- a/lib/dns/rdata/generic/mb_7.c +++ b/lib/dns/rdata/generic/mb_7.c @@ -179,11 +179,13 @@ additionaldata_mb(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_mb); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/md_3.c b/lib/dns/rdata/generic/md_3.c index 78c36f92ef..cff5e91c3c 100644 --- a/lib/dns/rdata/generic/md_3.c +++ b/lib/dns/rdata/generic/md_3.c @@ -180,11 +180,13 @@ additionaldata_md(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_md); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/mf_4.c b/lib/dns/rdata/generic/mf_4.c index 8d2c2b8482..13b63561fe 100644 --- a/lib/dns/rdata/generic/mf_4.c +++ b/lib/dns/rdata/generic/mf_4.c @@ -179,11 +179,13 @@ additionaldata_mf(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_mf); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/mg_8.c b/lib/dns/rdata/generic/mg_8.c index 0b60c90951..efaa0a34be 100644 --- a/lib/dns/rdata/generic/mg_8.c +++ b/lib/dns/rdata/generic/mg_8.c @@ -178,6 +178,7 @@ additionaldata_mg(ARGS_ADDLDATA) { UNUSED(add); UNUSED(arg); UNUSED(rdata); + UNUSED(owner); return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/minfo_14.c b/lib/dns/rdata/generic/minfo_14.c index 7394e3f292..62b5897bc6 100644 --- a/lib/dns/rdata/generic/minfo_14.c +++ b/lib/dns/rdata/generic/minfo_14.c @@ -253,6 +253,7 @@ additionaldata_minfo(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_minfo); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/mr_9.c b/lib/dns/rdata/generic/mr_9.c index 295cf6ed1a..6205358313 100644 --- a/lib/dns/rdata/generic/mr_9.c +++ b/lib/dns/rdata/generic/mr_9.c @@ -176,6 +176,7 @@ additionaldata_mr(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_mr); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/mx_15.c b/lib/dns/rdata/generic/mx_15.c index 9bddb45ee0..2b5fe03f54 100644 --- a/lib/dns/rdata/generic/mx_15.c +++ b/lib/dns/rdata/generic/mx_15.c @@ -273,6 +273,8 @@ additionaldata_mx(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_mx); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 2); @@ -282,7 +284,7 @@ additionaldata_mx(ARGS_ADDLDATA) { return (ISC_R_SUCCESS); } - result = (add)(arg, &name, dns_rdatatype_a); + result = (add)(arg, &name, dns_rdatatype_a, NULL); if (result != ISC_R_SUCCESS) { return (result); } @@ -294,7 +296,8 @@ additionaldata_mx(ARGS_ADDLDATA) { return (ISC_R_SUCCESS); } - return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa)); + return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa, + NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/naptr_35.c b/lib/dns/rdata/generic/naptr_35.c index 04bb8e05a8..d8de8e9def 100644 --- a/lib/dns/rdata/generic/naptr_35.c +++ b/lib/dns/rdata/generic/naptr_35.c @@ -601,6 +601,8 @@ additionaldata_naptr(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_naptr); + UNUSED(owner); + /* * Order, preference. */ @@ -642,7 +644,7 @@ additionaldata_naptr(ARGS_ADDLDATA) { dns_name_fromregion(&name, &sr); if (atype != 0) { - return ((add)(arg, &name, atype)); + return ((add)(arg, &name, atype, NULL)); } return (ISC_R_SUCCESS); diff --git a/lib/dns/rdata/generic/nid_104.c b/lib/dns/rdata/generic/nid_104.c index d05a42ca30..d8f9afa815 100644 --- a/lib/dns/rdata/generic/nid_104.c +++ b/lib/dns/rdata/generic/nid_104.c @@ -172,6 +172,7 @@ additionaldata_nid(ARGS_ADDLDATA) { REQUIRE(rdata->length == 10); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/ninfo_56.c b/lib/dns/rdata/generic/ninfo_56.c index 0e74d522f0..a14356c0c7 100644 --- a/lib/dns/rdata/generic/ninfo_56.c +++ b/lib/dns/rdata/generic/ninfo_56.c @@ -95,6 +95,7 @@ additionaldata_ninfo(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ninfo); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/ns_2.c b/lib/dns/rdata/generic/ns_2.c index 45a760c616..21ed02349e 100644 --- a/lib/dns/rdata/generic/ns_2.c +++ b/lib/dns/rdata/generic/ns_2.c @@ -190,11 +190,13 @@ additionaldata_ns(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ns); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/nsec3_50.c b/lib/dns/rdata/generic/nsec3_50.c index e1eab66d5a..8f62a61533 100644 --- a/lib/dns/rdata/generic/nsec3_50.c +++ b/lib/dns/rdata/generic/nsec3_50.c @@ -362,6 +362,7 @@ additionaldata_nsec3(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_nsec3); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/nsec3param_51.c b/lib/dns/rdata/generic/nsec3param_51.c index 1cc56e5ae2..db3a97413c 100644 --- a/lib/dns/rdata/generic/nsec3param_51.c +++ b/lib/dns/rdata/generic/nsec3param_51.c @@ -272,6 +272,7 @@ additionaldata_nsec3param(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_nsec3param); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/nsec_47.c b/lib/dns/rdata/generic/nsec_47.c index 004d163089..f7e4693c33 100644 --- a/lib/dns/rdata/generic/nsec_47.c +++ b/lib/dns/rdata/generic/nsec_47.c @@ -213,6 +213,7 @@ additionaldata_nsec(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_nsec); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/null_10.c b/lib/dns/rdata/generic/null_10.c index f495d56a72..f13d052e24 100644 --- a/lib/dns/rdata/generic/null_10.c +++ b/lib/dns/rdata/generic/null_10.c @@ -133,12 +133,13 @@ freestruct_null(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_null(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_null); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_null); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/nxt_30.c b/lib/dns/rdata/generic/nxt_30.c index 4065204fe1..d713f0192a 100644 --- a/lib/dns/rdata/generic/nxt_30.c +++ b/lib/dns/rdata/generic/nxt_30.c @@ -292,6 +292,7 @@ additionaldata_nxt(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_nxt); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/openpgpkey_61.c b/lib/dns/rdata/generic/openpgpkey_61.c index 7077eac71d..8c4bec53e4 100644 --- a/lib/dns/rdata/generic/openpgpkey_61.c +++ b/lib/dns/rdata/generic/openpgpkey_61.c @@ -187,6 +187,7 @@ additionaldata_openpgpkey(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_openpgpkey); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/opt_41.c b/lib/dns/rdata/generic/opt_41.c index 7dbb370386..6d6a127eaf 100644 --- a/lib/dns/rdata/generic/opt_41.c +++ b/lib/dns/rdata/generic/opt_41.c @@ -359,6 +359,7 @@ additionaldata_opt(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_opt); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/proforma.c b/lib/dns/rdata/generic/proforma.c index 7913d66162..1db5d9e469 100644 --- a/lib/dns/rdata/generic/proforma.c +++ b/lib/dns/rdata/generic/proforma.c @@ -103,8 +103,9 @@ static inline isc_result_t additionaldata_ #(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_proforma.c #); REQUIRE(rdata->rdclass == #); - (void)add; - (void)arg; + UNUSED(owner); + UNUSED(add); + UNUSED(arg); return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/ptr_12.c b/lib/dns/rdata/generic/ptr_12.c index 188676a660..bf689eb7c0 100644 --- a/lib/dns/rdata/generic/ptr_12.c +++ b/lib/dns/rdata/generic/ptr_12.c @@ -190,6 +190,7 @@ additionaldata_ptr(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ptr); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/rkey_57.c b/lib/dns/rdata/generic/rkey_57.c index 7dd11a29e4..afa294daae 100644 --- a/lib/dns/rdata/generic/rkey_57.c +++ b/lib/dns/rdata/generic/rkey_57.c @@ -105,6 +105,7 @@ additionaldata_rkey(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_rkey); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/rp_17.c b/lib/dns/rdata/generic/rp_17.c index 70c3204d23..a44a2321ed 100644 --- a/lib/dns/rdata/generic/rp_17.c +++ b/lib/dns/rdata/generic/rp_17.c @@ -252,6 +252,7 @@ additionaldata_rp(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_rp); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/rrsig_46.c b/lib/dns/rdata/generic/rrsig_46.c index 40506d7ed6..e6ef8725d1 100644 --- a/lib/dns/rdata/generic/rrsig_46.c +++ b/lib/dns/rdata/generic/rrsig_46.c @@ -536,6 +536,7 @@ additionaldata_rrsig(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_rrsig); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/rt_21.c b/lib/dns/rdata/generic/rt_21.c index e54d02970a..08fb097bf3 100644 --- a/lib/dns/rdata/generic/rt_21.c +++ b/lib/dns/rdata/generic/rt_21.c @@ -241,20 +241,22 @@ additionaldata_rt(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_rt); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 2); dns_name_fromregion(&name, ®ion); - result = (add)(arg, &name, dns_rdatatype_x25); + result = (add)(arg, &name, dns_rdatatype_x25, NULL); if (result != ISC_R_SUCCESS) { return (result); } - result = (add)(arg, &name, dns_rdatatype_isdn); + result = (add)(arg, &name, dns_rdatatype_isdn, NULL); if (result != ISC_R_SUCCESS) { return (result); } - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/generic/sig_24.c b/lib/dns/rdata/generic/sig_24.c index bfff78c663..fc9279eebe 100644 --- a/lib/dns/rdata/generic/sig_24.c +++ b/lib/dns/rdata/generic/sig_24.c @@ -528,6 +528,7 @@ additionaldata_sig(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_sig); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/sink_40.c b/lib/dns/rdata/generic/sink_40.c index 09bb04fab4..5d85652d9c 100644 --- a/lib/dns/rdata/generic/sink_40.c +++ b/lib/dns/rdata/generic/sink_40.c @@ -242,6 +242,7 @@ additionaldata_sink(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_sink); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/smimea_53.c b/lib/dns/rdata/generic/smimea_53.c index 8871369d9e..dd3894b30d 100644 --- a/lib/dns/rdata/generic/smimea_53.c +++ b/lib/dns/rdata/generic/smimea_53.c @@ -102,6 +102,7 @@ additionaldata_smimea(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_smimea); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/soa_6.c b/lib/dns/rdata/generic/soa_6.c index c30b079b58..c9fbc10afe 100644 --- a/lib/dns/rdata/generic/soa_6.c +++ b/lib/dns/rdata/generic/soa_6.c @@ -370,12 +370,13 @@ freestruct_soa(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_soa(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_soa); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_soa); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/spf_99.c b/lib/dns/rdata/generic/spf_99.c index 4687324e30..29d5c54fa5 100644 --- a/lib/dns/rdata/generic/spf_99.c +++ b/lib/dns/rdata/generic/spf_99.c @@ -96,6 +96,7 @@ additionaldata_spf(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_spf); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/sshfp_44.c b/lib/dns/rdata/generic/sshfp_44.c index 9c604c1380..8c91d8fbcc 100644 --- a/lib/dns/rdata/generic/sshfp_44.c +++ b/lib/dns/rdata/generic/sshfp_44.c @@ -246,6 +246,7 @@ additionaldata_sshfp(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_sshfp); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/ta_32768.c b/lib/dns/rdata/generic/ta_32768.c index 93e095d4ac..69875c36ab 100644 --- a/lib/dns/rdata/generic/ta_32768.c +++ b/lib/dns/rdata/generic/ta_32768.c @@ -112,6 +112,7 @@ additionaldata_ta(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_ta); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/talink_58.c b/lib/dns/rdata/generic/talink_58.c index bf001bc160..1682facae2 100644 --- a/lib/dns/rdata/generic/talink_58.c +++ b/lib/dns/rdata/generic/talink_58.c @@ -216,12 +216,13 @@ freestruct_talink(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_talink(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_talink); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_talink); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/tkey_249.c b/lib/dns/rdata/generic/tkey_249.c index 4eae9d9094..6ba242789d 100644 --- a/lib/dns/rdata/generic/tkey_249.c +++ b/lib/dns/rdata/generic/tkey_249.c @@ -527,12 +527,13 @@ freestruct_tkey(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_tkey(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_tkey); + UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_tkey); - return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/generic/tlsa_52.c b/lib/dns/rdata/generic/tlsa_52.c index 97ea972cbc..7ec2aaaa67 100644 --- a/lib/dns/rdata/generic/tlsa_52.c +++ b/lib/dns/rdata/generic/tlsa_52.c @@ -289,6 +289,7 @@ additionaldata_tlsa(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_tlsa); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/txt_16.c b/lib/dns/rdata/generic/txt_16.c index 643fb3dbd7..b0f1c0c9aa 100644 --- a/lib/dns/rdata/generic/txt_16.c +++ b/lib/dns/rdata/generic/txt_16.c @@ -231,6 +231,7 @@ additionaldata_txt(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_txt); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/uri_256.c b/lib/dns/rdata/generic/uri_256.c index ae53d85c4b..b66b583423 100644 --- a/lib/dns/rdata/generic/uri_256.c +++ b/lib/dns/rdata/generic/uri_256.c @@ -268,6 +268,7 @@ additionaldata_uri(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_uri); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/x25_19.c b/lib/dns/rdata/generic/x25_19.c index 0ba32d582f..ec27cecae6 100644 --- a/lib/dns/rdata/generic/x25_19.c +++ b/lib/dns/rdata/generic/x25_19.c @@ -182,6 +182,7 @@ additionaldata_x25(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_x25); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/generic/zonemd_63.c b/lib/dns/rdata/generic/zonemd_63.c index 8bb2478e35..e4dae5548e 100644 --- a/lib/dns/rdata/generic/zonemd_63.c +++ b/lib/dns/rdata/generic/zonemd_63.c @@ -300,6 +300,7 @@ additionaldata_zonemd(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_zonemd); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/hs_4/a_1.c b/lib/dns/rdata/hs_4/a_1.c index 6a4c1a53bf..1df54fc6f6 100644 --- a/lib/dns/rdata/hs_4/a_1.c +++ b/lib/dns/rdata/hs_4/a_1.c @@ -182,6 +182,7 @@ additionaldata_hs_a(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_hs); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/a6_38.c b/lib/dns/rdata/in_1/a6_38.c index 377c4ec82f..5828ddb39c 100644 --- a/lib/dns/rdata/in_1/a6_38.c +++ b/lib/dns/rdata/in_1/a6_38.c @@ -401,6 +401,7 @@ additionaldata_in_a6(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/a_1.c b/lib/dns/rdata/in_1/a_1.c index 24e303f257..cf544f0279 100644 --- a/lib/dns/rdata/in_1/a_1.c +++ b/lib/dns/rdata/in_1/a_1.c @@ -186,6 +186,7 @@ additionaldata_in_a(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/aaaa_28.c b/lib/dns/rdata/in_1/aaaa_28.c index d7427a1757..5ac6d649db 100644 --- a/lib/dns/rdata/in_1/aaaa_28.c +++ b/lib/dns/rdata/in_1/aaaa_28.c @@ -198,6 +198,7 @@ additionaldata_in_aaaa(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/apl_42.c b/lib/dns/rdata/in_1/apl_42.c index 50b784c15d..5ba4527b83 100644 --- a/lib/dns/rdata/in_1/apl_42.c +++ b/lib/dns/rdata/in_1/apl_42.c @@ -428,8 +428,10 @@ additionaldata_in_apl(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_apl); REQUIRE(rdata->rdclass == dns_rdataclass_in); - (void)add; - (void)arg; + UNUSED(rdata); + UNUSED(owner); + UNUSED(add); + UNUSED(arg); return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdata/in_1/atma_34.c b/lib/dns/rdata/in_1/atma_34.c index 07a9198e53..11fb203267 100644 --- a/lib/dns/rdata/in_1/atma_34.c +++ b/lib/dns/rdata/in_1/atma_34.c @@ -264,6 +264,7 @@ additionaldata_in_atma(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/dhcid_49.c b/lib/dns/rdata/in_1/dhcid_49.c index 454bf58292..8353e69f10 100644 --- a/lib/dns/rdata/in_1/dhcid_49.c +++ b/lib/dns/rdata/in_1/dhcid_49.c @@ -182,6 +182,7 @@ additionaldata_in_dhcid(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/eid_31.c b/lib/dns/rdata/in_1/eid_31.c index fdab429e50..454ad2d570 100644 --- a/lib/dns/rdata/in_1/eid_31.c +++ b/lib/dns/rdata/in_1/eid_31.c @@ -171,6 +171,7 @@ additionaldata_in_eid(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/https_65.c b/lib/dns/rdata/in_1/https_65.c new file mode 100644 index 0000000000..ff562bec6d --- /dev/null +++ b/lib/dns/rdata/in_1/https_65.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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. + */ + +/* draft-ietf-dnsop-svcb-https-02 */ + +#ifndef RDATA_IN_1_HTTPS_65_C +#define RDATA_IN_1_HTTPS_65_C + +#define RRTYPE_HTTPS_ATTRIBUTES (DNS_RDATATYPEATTR_FOLLOWADDITIONAL) + +/* + * Most of these functions refer to equivalent functions for SVCB, + * since wire and presentation formats are identical. + */ + +static inline isc_result_t +fromtext_in_https(ARGS_FROMTEXT) { + REQUIRE(type == dns_rdatatype_https); + REQUIRE(rdclass == dns_rdataclass_in); + + return (generic_fromtext_in_svcb(CALL_FROMTEXT)); +} + +static inline isc_result_t +totext_in_https(ARGS_TOTEXT) { + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + REQUIRE(rdata->length != 0); + + return (generic_totext_in_svcb(CALL_TOTEXT)); +} + +static inline isc_result_t +fromwire_in_https(ARGS_FROMWIRE) { + REQUIRE(type == dns_rdatatype_https); + REQUIRE(rdclass == dns_rdataclass_in); + + return (generic_fromwire_in_svcb(CALL_FROMWIRE)); +} + +static inline isc_result_t +towire_in_https(ARGS_TOWIRE) { + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(rdata->length != 0); + + return (generic_towire_in_svcb(CALL_TOWIRE)); +} + +static inline int +compare_in_https(ARGS_COMPARE) { + isc_region_t region1; + isc_region_t region2; + + REQUIRE(rdata1->type == rdata2->type); + REQUIRE(rdata1->rdclass == rdata2->rdclass); + REQUIRE(rdata1->type == dns_rdatatype_https); + REQUIRE(rdata1->rdclass == dns_rdataclass_in); + REQUIRE(rdata1->length != 0); + REQUIRE(rdata2->length != 0); + + dns_rdata_toregion(rdata1, ®ion1); + dns_rdata_toregion(rdata2, ®ion2); + + return (isc_region_compare(®ion1, ®ion2)); +} + +static inline isc_result_t +fromstruct_in_https(ARGS_FROMSTRUCT) { + dns_rdata_in_https_t *https = source; + + REQUIRE(type == dns_rdatatype_https); + REQUIRE(rdclass == dns_rdataclass_in); + REQUIRE(https != NULL); + REQUIRE(https->common.rdtype == type); + REQUIRE(https->common.rdclass == rdclass); + + return (generic_fromstruct_in_svcb(CALL_FROMSTRUCT)); +} + +static inline isc_result_t +tostruct_in_https(ARGS_TOSTRUCT) { + dns_rdata_in_https_t *https = target; + + REQUIRE(rdata->rdclass == dns_rdataclass_in); + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(https != NULL); + REQUIRE(rdata->length != 0); + + return (generic_tostruct_in_svcb(CALL_TOSTRUCT)); +} + +static inline void +freestruct_in_https(ARGS_FREESTRUCT) { + dns_rdata_in_https_t *https = source; + + REQUIRE(https != NULL); + REQUIRE(https->common.rdclass == dns_rdataclass_in); + REQUIRE(https->common.rdtype == dns_rdatatype_https); + + generic_freestruct_in_svcb(CALL_FREESTRUCT); +} + +static inline isc_result_t +additionaldata_in_https(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + return (generic_additionaldata_in_svcb(CALL_ADDLDATA)); +} + +static inline isc_result_t +digest_in_https(ARGS_DIGEST) { + isc_region_t region1; + + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + dns_rdata_toregion(rdata, ®ion1); + return ((digest)(arg, ®ion1)); +} + +static inline bool +checkowner_in_https(ARGS_CHECKOWNER) { + REQUIRE(type == dns_rdatatype_https); + REQUIRE(rdclass == dns_rdataclass_in); + + UNUSED(name); + UNUSED(type); + UNUSED(rdclass); + UNUSED(wildcard); + + return (true); +} + +static inline bool +checknames_in_https(ARGS_CHECKNAMES) { + REQUIRE(rdata->type == dns_rdatatype_https); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + return (generic_checknames_in_svcb(CALL_CHECKNAMES)); +} + +static inline int +casecompare_in_https(ARGS_COMPARE) { + return (compare_in_https(rdata1, rdata2)); +} + +isc_result_t +dns_rdata_in_https_first(dns_rdata_in_https_t *https) { + REQUIRE(https != NULL); + REQUIRE(https->common.rdtype == dns_rdatatype_https); + REQUIRE(https->common.rdclass == dns_rdataclass_in); + + return (generic_rdata_in_svcb_first(https)); +} + +isc_result_t +dns_rdata_in_https_next(dns_rdata_in_https_t *https) { + REQUIRE(https != NULL); + REQUIRE(https->common.rdtype == dns_rdatatype_https); + REQUIRE(https->common.rdclass == dns_rdataclass_in); + + return (generic_rdata_in_svcb_next(https)); +} + +void +dns_rdata_in_https_current(dns_rdata_in_https_t *https, isc_region_t *region) { + REQUIRE(https != NULL); + REQUIRE(https->common.rdtype == dns_rdatatype_https); + REQUIRE(https->common.rdclass == dns_rdataclass_in); + REQUIRE(region != NULL); + + generic_rdata_in_svcb_current(https, region); +} + +#endif /* RDATA_IN_1_HTTPS_65_C */ diff --git a/lib/dns/rdata/in_1/https_65.h b/lib/dns/rdata/in_1/https_65.h new file mode 100644 index 0000000000..2469a35962 --- /dev/null +++ b/lib/dns/rdata/in_1/https_65.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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. + */ + +#ifndef IN_1_HTTPS_65_H +#define IN_1_HTTPS_65_H 1 + +/*! + * \brief Per draft-ietf-dnsop-svcb-https-02 + */ + +/* + * Wire and presentation formats for HTTPS are identical to SVCB. + */ +typedef struct dns_rdata_in_svcb dns_rdata_in_https_t; + +isc_result_t +dns_rdata_in_https_first(dns_rdata_in_https_t *); + +isc_result_t +dns_rdata_in_https_next(dns_rdata_in_https_t *); + +void +dns_rdata_in_https_current(dns_rdata_in_https_t *, isc_region_t *); + +#endif /* IN_1_HTTPS_65_H */ diff --git a/lib/dns/rdata/in_1/kx_36.c b/lib/dns/rdata/in_1/kx_36.c index 2b47aa919f..bfa8d71771 100644 --- a/lib/dns/rdata/in_1/kx_36.c +++ b/lib/dns/rdata/in_1/kx_36.c @@ -228,12 +228,14 @@ additionaldata_in_kx(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_kx); REQUIRE(rdata->rdclass == dns_rdataclass_in); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 2); dns_name_fromregion(&name, ®ion); - return ((add)(arg, &name, dns_rdatatype_a)); + return ((add)(arg, &name, dns_rdatatype_a, NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/in_1/nimloc_32.c b/lib/dns/rdata/in_1/nimloc_32.c index 751e5a8918..ce4cc5f2af 100644 --- a/lib/dns/rdata/in_1/nimloc_32.c +++ b/lib/dns/rdata/in_1/nimloc_32.c @@ -171,6 +171,7 @@ additionaldata_in_nimloc(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/nsap-ptr_23.c b/lib/dns/rdata/in_1/nsap-ptr_23.c index ea7bd3b6ae..9960ce818c 100644 --- a/lib/dns/rdata/in_1/nsap-ptr_23.c +++ b/lib/dns/rdata/in_1/nsap-ptr_23.c @@ -187,6 +187,7 @@ additionaldata_in_nsap_ptr(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/nsap_22.c b/lib/dns/rdata/in_1/nsap_22.c index fe3361ce51..fa2d3d6d3b 100644 --- a/lib/dns/rdata/in_1/nsap_22.c +++ b/lib/dns/rdata/in_1/nsap_22.c @@ -206,6 +206,7 @@ additionaldata_in_nsap(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/px_26.c b/lib/dns/rdata/in_1/px_26.c index 73d595355e..02faa728bc 100644 --- a/lib/dns/rdata/in_1/px_26.c +++ b/lib/dns/rdata/in_1/px_26.c @@ -308,6 +308,7 @@ additionaldata_in_px(ARGS_ADDLDATA) { REQUIRE(rdata->rdclass == dns_rdataclass_in); UNUSED(rdata); + UNUSED(owner); UNUSED(add); UNUSED(arg); diff --git a/lib/dns/rdata/in_1/srv_33.c b/lib/dns/rdata/in_1/srv_33.c index 268c0e9385..2a299cedf7 100644 --- a/lib/dns/rdata/in_1/srv_33.c +++ b/lib/dns/rdata/in_1/srv_33.c @@ -14,7 +14,7 @@ #ifndef RDATA_IN_1_SRV_33_C #define RDATA_IN_1_SRV_33_C -#define RRTYPE_SRV_ATTRIBUTES (0) +#define RRTYPE_SRV_ATTRIBUTES (DNS_RDATATYPEATTR_FOLLOWADDITIONAL) static inline isc_result_t fromtext_in_srv(ARGS_FROMTEXT) { @@ -313,6 +313,8 @@ additionaldata_in_srv(ARGS_ADDLDATA) { REQUIRE(rdata->type == dns_rdatatype_srv); REQUIRE(rdata->rdclass == dns_rdataclass_in); + UNUSED(owner); + dns_name_init(&name, offsets); dns_rdata_toregion(rdata, ®ion); isc_region_consume(®ion, 4); @@ -324,7 +326,7 @@ additionaldata_in_srv(ARGS_ADDLDATA) { return (ISC_R_SUCCESS); } - result = (add)(arg, &name, dns_rdatatype_a); + result = (add)(arg, &name, dns_rdatatype_a, NULL); if (result != ISC_R_SUCCESS) { return (result); } @@ -343,7 +345,8 @@ additionaldata_in_srv(ARGS_ADDLDATA) { return (ISC_R_SUCCESS); } - return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa)); + return ((add)(arg, dns_fixedname_name(&fixed), dns_rdatatype_tlsa, + NULL)); } static inline isc_result_t diff --git a/lib/dns/rdata/in_1/svcb_64.c b/lib/dns/rdata/in_1/svcb_64.c new file mode 100644 index 0000000000..2af6de074c --- /dev/null +++ b/lib/dns/rdata/in_1/svcb_64.c @@ -0,0 +1,1285 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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. + */ + +/* draft-ietf-dnsop-svcb-https-02 */ + +#ifndef RDATA_IN_1_SVCB_64_C +#define RDATA_IN_1_SVCB_64_C + +#define RRTYPE_SVCB_ATTRIBUTES (DNS_RDATATYPEATTR_FOLLOWADDITIONAL) + +#define SVCB_MAN_KEY 0 +#define SVCB_ALPN_KEY 1 +#define SVCB_NO_DEFAULT_ALPN_KEY 2 +#define MAX_CNAMES 16 /* See ns/query.c MAX_RESTARTS */ + +/* + * Service Binding Parameter Registry + */ +enum encoding { + sbpr_text, + sbpr_port, + sbpr_ipv4s, + sbpr_ipv6s, + sbpr_base64, + sbpr_empty, + sbpr_alpn, + sbpr_keylist +}; +static const struct { + const char *name; /* Restricted to lowercase LDH by registry. */ + unsigned int value; + enum encoding encoding; + bool initial; +} sbpr[] = { + { "mandatory", 0, sbpr_keylist, true }, + { "alpn", 1, sbpr_alpn, true }, + { "no-default-alpn", 2, sbpr_empty, true }, + { "port", 3, sbpr_port, true }, + { "ipv4hint", 4, sbpr_ipv4s, true }, + { "ech", 5, sbpr_base64, true }, + { "ipv6hint", 6, sbpr_ipv6s, true }, +}; + +static isc_result_t +alpn_fromtxt(isc_textregion_t *source, isc_buffer_t *target) { + isc_textregion_t source0 = *source; + do { + RETERR(commatxt_fromtext(&source0, true, target)); + } while (source0.length != 0); + return (ISC_R_SUCCESS); +} + +static int +svckeycmp(const void *a1, const void *a2) { + const unsigned char *u1 = a1, *u2 = a2; + if (*u1 != *u2) { + return (*u1 - *u2); + } + return (*(++u1) - *(++u2)); +} + +static isc_result_t +svcsortkeylist(isc_buffer_t *target, unsigned int used) { + isc_region_t region; + + isc_buffer_usedregion(target, ®ion); + isc_region_consume(®ion, used); + INSIST(region.length > 0U); + qsort(region.base, region.length / 2, 2, svckeycmp); + /* Reject duplicates. */ + while (region.length >= 4) { + if (region.base[0] == region.base[2] && + region.base[1] == region.base[3]) { + return (DNS_R_SYNTAX); + } + isc_region_consume(®ion, 2); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +svcb_validate(uint16_t key, isc_region_t *region) { + size_t i; + +#ifndef ARRAYSIZE +/* defined in winnt.h */ +#define ARRAYSIZE(x) (sizeof(x) / sizeof(*x)) +#endif + + for (i = 0; i < ARRAYSIZE(sbpr); i++) { + if (sbpr[i].value == key) { + switch (sbpr[i].encoding) { + case sbpr_port: + if (region->length != 2) { + return (DNS_R_FORMERR); + } + break; + case sbpr_ipv4s: + if ((region->length % 4) != 0 || + region->length == 0) { + return (DNS_R_FORMERR); + } + break; + case sbpr_ipv6s: + if ((region->length % 16) != 0 || + region->length == 0) { + return (DNS_R_FORMERR); + } + break; + case sbpr_alpn: { + if (region->length == 0) { + return (DNS_R_FORMERR); + } + while (region->length != 0) { + size_t l = *region->base + 1; + if (l > region->length) { + return (DNS_R_FORMERR); + } + isc_region_consume(region, l); + } + break; + } + case sbpr_keylist: { + if ((region->length % 2) != 0 || + region->length == 0) { + return (DNS_R_FORMERR); + } + /* In order? */ + while (region->length >= 4) { + if (region->base[0] > region->base[2] || + (region->base[0] == + region->base[2] && + region->base[1] >= + region->base[3])) + { + return (DNS_R_FORMERR); + } + isc_region_consume(region, 2); + } + break; + } + case sbpr_text: + case sbpr_base64: + break; + case sbpr_empty: + if (region->length != 0) { + return (DNS_R_FORMERR); + } + break; + } + } + } + return (ISC_R_SUCCESS); +} + +/* + * Parse keyname from region. + */ +static isc_result_t +svc_keyfromregion(isc_textregion_t *region, char sep, uint16_t *value, + isc_buffer_t *target) { + char *e = NULL; + size_t i; + unsigned long ul; + + /* Look for known key names. */ + for (i = 0; i < ARRAYSIZE(sbpr); i++) { + size_t len = strlen(sbpr[i].name); + if (strncasecmp(region->base, sbpr[i].name, len) != 0 || + (region->base[len] != 0 && region->base[len] != sep)) + { + continue; + } + isc_textregion_consume(region, len); + ul = sbpr[i].value; + goto finish; + } + /* Handle keyXXXXX form. */ + if (strncmp(region->base, "key", 3) != 0) { + return (DNS_R_SYNTAX); + } + isc_textregion_consume(region, 3); + /* Disallow [+-]XXXXX which is allowed by strtoul. */ + if (region->length == 0 || *region->base == '-' || *region->base == '+') + { + return (DNS_R_SYNTAX); + } + /* No zero padding. */ + if (region->length > 1 && *region->base == '0' && + region->base[1] != sep) { + return (DNS_R_SYNTAX); + } + ul = strtoul(region->base, &e, 10); + /* Valid number? */ + if (e == region->base || (*e != sep && *e != 0)) { + return (DNS_R_SYNTAX); + } + if (ul > 0xffff) { + return (ISC_R_RANGE); + } + isc_textregion_consume(region, e - region->base); +finish: + if (sep == ',' && region->length == 1) { + return (DNS_R_SYNTAX); + } + /* Consume separator. */ + if (region->length != 0) { + isc_textregion_consume(region, 1); + } + RETERR(uint16_tobuffer(ul, target)); + if (value != NULL) { + *value = ul; + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +svc_fromtext(isc_textregion_t *region, isc_buffer_t *target) { + char *e = NULL; + char abuf[16]; + char tbuf[sizeof("aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:255.255.255.255,")]; + isc_buffer_t sb; + isc_region_t keyregion; + size_t len; + uint16_t key; + unsigned int i; + unsigned int used; + unsigned long ul; + + for (i = 0; i < ARRAYSIZE(sbpr); i++) { + len = strlen(sbpr[i].name); + if (strncmp(region->base, sbpr[i].name, len) != 0 || + (region->base[len] != 0 && region->base[len] != '=')) + { + continue; + } + + if (region->base[len] == '=') { + len++; + } + + RETERR(uint16_tobuffer(sbpr[i].value, target)); + isc_textregion_consume(region, len); + + sb = *target; + RETERR(uint16_tobuffer(0, target)); /* length */ + + switch (sbpr[i].encoding) { + case sbpr_text: + RETERR(multitxt_fromtext(region, target)); + break; + case sbpr_alpn: + RETERR(alpn_fromtxt(region, target)); + break; + case sbpr_port: + if (!isdigit(*region->base)) { + return (DNS_R_SYNTAX); + } + ul = strtoul(region->base, &e, 10); + if (*e != '\0') { + return (DNS_R_SYNTAX); + } + if (ul > 0xffff) { + return (ISC_R_RANGE); + } + RETERR(uint16_tobuffer(ul, target)); + break; + case sbpr_ipv4s: + do { + snprintf(tbuf, sizeof(tbuf), "%*s", + (int)(region->length), region->base); + e = strchr(tbuf, ','); + if (e != NULL) { + *e++ = 0; + isc_textregion_consume(region, + e - tbuf); + } + if (inet_pton(AF_INET, tbuf, abuf) != 1) { + return (DNS_R_SYNTAX); + } + mem_tobuffer(target, abuf, 4); + } while (e != NULL); + break; + case sbpr_ipv6s: + do { + snprintf(tbuf, sizeof(tbuf), "%*s", + (int)(region->length), region->base); + e = strchr(tbuf, ','); + if (e != NULL) { + *e++ = 0; + isc_textregion_consume(region, + e - tbuf); + } + if (inet_pton(AF_INET6, tbuf, abuf) != 1) { + return (DNS_R_SYNTAX); + } + mem_tobuffer(target, abuf, 16); + } while (e != NULL); + break; + case sbpr_base64: + RETERR(isc_base64_decodestring(region->base, target)); + break; + case sbpr_empty: + if (region->length != 0) { + return (DNS_R_SYNTAX); + } + break; + case sbpr_keylist: + if (region->length == 0) { + return (DNS_R_SYNTAX); + } + used = isc_buffer_usedlength(target); + while (region->length != 0) { + RETERR(svc_keyfromregion(region, ',', NULL, + target)); + } + RETERR(svcsortkeylist(target, used)); + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + + len = isc_buffer_usedlength(target) - + isc_buffer_usedlength(&sb) - 2; + RETERR(uint16_tobuffer(len, &sb)); /* length */ + return (ISC_R_SUCCESS); + } + + RETERR(svc_keyfromregion(region, '=', &key, target)); + if (region->length == 0) { + RETERR(uint16_tobuffer(0, target)); /* length */ + /* Sanity check keyXXXXX form. */ + keyregion.base = isc_buffer_used(target); + keyregion.length = 0; + return (svcb_validate(key, &keyregion)); + } + sb = *target; + RETERR(uint16_tobuffer(0, target)); /* dummy length */ + RETERR(multitxt_fromtext(region, target)); + len = isc_buffer_usedlength(target) - isc_buffer_usedlength(&sb) - 2; + RETERR(uint16_tobuffer(len, &sb)); /* length */ + /* Sanity check keyXXXXX form. */ + keyregion.base = isc_buffer_used(&sb); + keyregion.length = len; + return (svcb_validate(key, &keyregion)); +} + +static const char * +svcparamkey(unsigned short value, enum encoding *encoding, char *buf, + size_t len) { + size_t i; + int n; + + for (i = 0; i < ARRAYSIZE(sbpr); i++) { + if (sbpr[i].value == value && sbpr[i].initial) { + *encoding = sbpr[i].encoding; + return (sbpr[i].name); + } + } + n = snprintf(buf, len, "key%u", value); + INSIST(n > 0 && (unsigned)n < len); + *encoding = sbpr_text; + return (buf); +} + +static isc_result_t +svcsortkeys(isc_buffer_t *target, unsigned int used) { + isc_region_t r1, r2, man = { .base = NULL, .length = 0 }; + unsigned char buf[1024]; + uint16_t mankey = 0; + bool have_alpn = false; + + if (isc_buffer_usedlength(target) == used) { + return (ISC_R_SUCCESS); + } + + /* + * Get the parameters into r1. + */ + isc_buffer_usedregion(target, &r1); + isc_region_consume(&r1, used); + + while (1) { + uint16_t key1, len1, key2, len2; + unsigned char *base1, *base2; + + r2 = r1; + + /* + * Get the first parameter. + */ + base1 = r1.base; + key1 = uint16_fromregion(&r1); + isc_region_consume(&r1, 2); + len1 = uint16_fromregion(&r1); + isc_region_consume(&r1, 2); + isc_region_consume(&r1, len1); + + /* + * Was there only one key left? + */ + if (r1.length == 0) { + if (mankey != 0) { + /* Is this the last mandatory key? */ + if (key1 != mankey || man.length != 0) { + return (DNS_R_INCONSISTENTRR); + } + } else if (key1 == SVCB_MAN_KEY) { + /* Lone mandatory field. */ + return (DNS_R_DISALLOWED); + } else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY && + !have_alpn) { + /* Missing required ALPN field. */ + return (DNS_R_DISALLOWED); + } + return (ISC_R_SUCCESS); + } + + /* + * Find the smallest parameter. + */ + while (r1.length != 0) { + base2 = r1.base; + key2 = uint16_fromregion(&r1); + isc_region_consume(&r1, 2); + len2 = uint16_fromregion(&r1); + isc_region_consume(&r1, 2); + isc_region_consume(&r1, len2); + if (key2 == key1) { + return (DNS_R_DUPLICATE); + } + if (key2 < key1) { + base1 = base2; + key1 = key2; + len1 = len2; + } + } + + /* + * Do we need to move the smallest parameter to the start? + */ + if (base1 != r2.base) { + size_t offset = 0; + size_t bytes = len1 + 4; + size_t length = base1 - r2.base; + + /* + * Move the smallest parameter to the start. + */ + while (bytes > 0) { + size_t count; + + if (bytes > sizeof(buf)) { + count = sizeof(buf); + } else { + count = bytes; + } + memmove(buf, base1, count); + memmove(r2.base + offset + count, + r2.base + offset, length); + memmove(r2.base + offset, buf, count); + base1 += count; + bytes -= count; + offset += count; + } + } + + /* + * Check ALPN is present when NO-DEFAULT-ALPN is set. + */ + if (key1 == SVCB_ALPN_KEY) { + have_alpn = true; + } else if (key1 == SVCB_NO_DEFAULT_ALPN_KEY && !have_alpn) { + /* Missing required ALPN field. */ + return (DNS_R_DISALLOWED); + } + + /* + * Check key against mandatory key list. + */ + if (mankey != 0) { + if (key1 > mankey) { + return (DNS_R_INCONSISTENTRR); + } + if (key1 == mankey) { + if (man.length >= 2) { + mankey = uint16_fromregion(&man); + isc_region_consume(&man, 2); + } else { + mankey = 0; + } + } + } + + /* + * Is this the mandatory key? + */ + if (key1 == SVCB_MAN_KEY) { + man = r2; + man.length = len1 + 4; + isc_region_consume(&man, 4); + if (man.length >= 2) { + mankey = uint16_fromregion(&man); + isc_region_consume(&man, 2); + if (mankey == SVCB_MAN_KEY) { + return (DNS_R_DISALLOWED); + } + } else { + return (DNS_R_SYNTAX); + } + } + + /* + * Consume the smallest parameter. + */ + isc_region_consume(&r2, len1 + 4); + r1 = r2; + } +} + +static inline isc_result_t +generic_fromtext_in_svcb(ARGS_FROMTEXT) { + isc_token_t token; + dns_name_t name; + isc_buffer_t buffer; + bool alias; + bool ok = true; + unsigned int used; + + UNUSED(type); + UNUSED(rdclass); + UNUSED(callbacks); + + /* + * SvcPriority. + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, + false)); + if (token.value.as_ulong > 0xffffU) { + RETTOK(ISC_R_RANGE); + } + RETERR(uint16_tobuffer(token.value.as_ulong, target)); + + alias = token.value.as_ulong == 0; + + /* + * TargetName. + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_qstring, + false)); + dns_name_init(&name, NULL); + buffer_fromregion(&buffer, &token.value.as_region); + if (origin == NULL) { + origin = dns_rootname; + } + RETTOK(dns_name_fromtext(&name, &buffer, origin, options, target)); + if (!alias && (options & DNS_RDATA_CHECKNAMES) != 0) { + ok = dns_name_ishostname(&name, false); + } + if (!ok && (options & DNS_RDATA_CHECKNAMESFAIL) != 0) { + RETTOK(DNS_R_BADNAME); + } + if (!ok && callbacks != NULL) { + warn_badname(&name, lexer, callbacks); + } + + if (alias) { + return (ISC_R_SUCCESS); + } + + /* + * SvcParams + */ + used = isc_buffer_usedlength(target); + while (1) { + RETERR(isc_lex_getmastertoken(lexer, &token, + isc_tokentype_qvpair, true)); + if (token.type == isc_tokentype_eol || + token.type == isc_tokentype_eof) { + isc_lex_ungettoken(lexer, &token); + return (svcsortkeys(target, used)); + } + + if (token.type != isc_tokentype_string && /* key only */ + token.type != isc_tokentype_qvpair && + token.type != isc_tokentype_vpair) + { + RETTOK(DNS_R_SYNTAX); + } + RETTOK(svc_fromtext(&token.value.as_textregion, target)); + } +} + +static inline isc_result_t +fromtext_in_svcb(ARGS_FROMTEXT) { + REQUIRE(type == dns_rdatatype_svcb); + REQUIRE(rdclass == dns_rdataclass_in); + UNUSED(type); + UNUSED(rdclass); + UNUSED(callbacks); + + return (generic_fromtext_in_svcb(CALL_FROMTEXT)); +} + +static inline isc_result_t +generic_totext_in_svcb(ARGS_TOTEXT) { + isc_region_t region; + dns_name_t name; + dns_name_t prefix; + bool sub; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; + unsigned short num; + int n; + + REQUIRE(rdata->length != 0); + + dns_name_init(&name, NULL); + dns_name_init(&prefix, NULL); + + dns_rdata_toregion(rdata, ®ion); + + /* + * SvcPriority. + */ + num = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + n = snprintf(buf, sizeof(buf), "%u ", num); + INSIST(n > 0 && (unsigned)n < sizeof(buf)); + RETERR(str_totext(buf, target)); + + /* + * TargetName. + */ + dns_name_fromregion(&name, ®ion); + isc_region_consume(®ion, name_length(&name)); + sub = name_prefix(&name, tctx->origin, &prefix); + RETERR(dns_name_totext(&prefix, sub, target)); + + while (region.length > 0) { + isc_region_t r; + enum encoding encoding; + + RETERR(str_totext(" ", target)); + + INSIST(region.length >= 2); + num = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + RETERR(str_totext(svcparamkey(num, &encoding, buf, sizeof(buf)), + target)); + + INSIST(region.length >= 2); + num = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + + INSIST(region.length >= num); + r = region; + r.length = num; + isc_region_consume(®ion, num); + if (num == 0) { + continue; + } + if (encoding != sbpr_empty) { + RETERR(str_totext("=", target)); + } + switch (encoding) { + case sbpr_text: + RETERR(multitxt_totext(&r, target)); + break; + case sbpr_port: + num = uint16_fromregion(&r); + isc_region_consume(&r, 2); + n = snprintf(buf, sizeof(buf), "%u", num); + INSIST(n > 0 && (unsigned)n < sizeof(buf)); + RETERR(str_totext(buf, target)); + INSIST(r.length == 0U); + break; + case sbpr_ipv4s: + while (r.length > 0U) { + INSIST(r.length >= 4U); + inet_ntop(AF_INET, r.base, buf, sizeof(buf)); + RETERR(str_totext(buf, target)); + isc_region_consume(&r, 4); + if (r.length != 0U) { + RETERR(str_totext(",", target)); + } + } + break; + case sbpr_ipv6s: + while (r.length > 0U) { + INSIST(r.length >= 16U); + inet_ntop(AF_INET6, r.base, buf, sizeof(buf)); + RETERR(str_totext(buf, target)); + isc_region_consume(&r, 16); + if (r.length != 0U) { + RETERR(str_totext(",", target)); + } + } + break; + case sbpr_base64: + RETERR(isc_base64_totext(&r, 0, "", target)); + break; + case sbpr_alpn: + INSIST(r.length != 0U); + RETERR(str_totext("\"", target)); + while (r.length != 0) { + commatxt_totext(&r, false, true, target); + if (r.length != 0) { + RETERR(str_totext(",", target)); + } + } + RETERR(str_totext("\"", target)); + break; + case sbpr_empty: + INSIST(r.length == 0U); + break; + case sbpr_keylist: + while (r.length > 0) { + num = uint16_fromregion(&r); + isc_region_consume(&r, 2); + RETERR(str_totext(svcparamkey(num, &encoding, + buf, sizeof(buf)), + target)); + if (r.length != 0) { + RETERR(str_totext(",", target)); + } + } + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + } + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +totext_in_svcb(ARGS_TOTEXT) { + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + REQUIRE(rdata->length != 0); + + return (generic_totext_in_svcb(CALL_TOTEXT)); +} + +static inline isc_result_t +generic_fromwire_in_svcb(ARGS_FROMWIRE) { + dns_name_t name; + isc_region_t region, man = { .base = NULL, .length = 0 }; + bool alias, first = true; + uint16_t lastkey = 0, mankey = 0; + + UNUSED(type); + UNUSED(rdclass); + + dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE); + + dns_name_init(&name, NULL); + + /* + * SvcPriority. + */ + isc_buffer_activeregion(source, ®ion); + if (region.length < 2) { + return (ISC_R_UNEXPECTEDEND); + } + RETERR(mem_tobuffer(target, region.base, 2)); + alias = uint16_fromregion(®ion) == 0; + isc_buffer_forward(source, 2); + + /* + * TargetName. + */ + RETERR(dns_name_fromwire(&name, source, dctx, options, target)); + + if (alias) { + return (ISC_R_SUCCESS); + } + + /* + * SvcParams. + */ + isc_buffer_activeregion(source, ®ion); + while (region.length > 0U) { + isc_region_t keyregion; + uint16_t key, len; + + /* + * SvcParamKey + */ + if (region.length < 2U) { + return (ISC_R_UNEXPECTEDEND); + } + RETERR(mem_tobuffer(target, region.base, 2)); + key = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + + /* + * Keys must be unique and in order. + */ + if (!first && key <= lastkey) { + return (DNS_R_FORMERR); + } + + /* + * Check mandatory keys. + */ + if (mankey != 0) { + /* Missing mandatory key? */ + if (key > mankey) { + return (DNS_R_FORMERR); + } + if (key == mankey) { + /* Get next mandatory key. */ + if (man.length >= 2) { + mankey = uint16_fromregion(&man); + isc_region_consume(&man, 2); + } else { + mankey = 0; + } + } + } + + first = false; + lastkey = key; + + /* + * SvcParamValue length. + */ + if (region.length < 2U) { + return (ISC_R_UNEXPECTEDEND); + } + RETERR(mem_tobuffer(target, region.base, 2)); + len = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + + /* + * SvcParamValue. + */ + if (region.length < len) { + return (ISC_R_UNEXPECTEDEND); + } + + /* + * Remember manatory key. + */ + if (key == SVCB_MAN_KEY) { + man = region; + man.length = len; + /* Get first mandatory key */ + if (man.length >= 2) { + mankey = uint16_fromregion(&man); + isc_region_consume(&man, 2); + if (mankey == SVCB_MAN_KEY) { + return (DNS_R_FORMERR); + } + } else { + return (DNS_R_FORMERR); + } + } + keyregion = region; + keyregion.length = len; + RETERR(svcb_validate(key, &keyregion)); + RETERR(mem_tobuffer(target, region.base, len)); + isc_region_consume(®ion, len); + isc_buffer_forward(source, len + 4); + } + + /* + * Do we have an outstanding mandatory key? + */ + if (mankey != 0) { + return (DNS_R_FORMERR); + } + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +fromwire_in_svcb(ARGS_FROMWIRE) { + REQUIRE(type == dns_rdatatype_svcb); + REQUIRE(rdclass == dns_rdataclass_in); + + return (generic_fromwire_in_svcb(CALL_FROMWIRE)); +} + +static inline isc_result_t +generic_towire_in_svcb(ARGS_TOWIRE) { + dns_name_t name; + dns_offsets_t offsets; + isc_region_t region; + + REQUIRE(rdata->length != 0); + + dns_compress_setmethods(cctx, DNS_COMPRESS_NONE); + + /* + * SvcPriority. + */ + dns_rdata_toregion(rdata, ®ion); + RETERR(mem_tobuffer(target, region.base, 2)); + isc_region_consume(®ion, 2); + + /* + * TargetName. + */ + dns_name_init(&name, offsets); + dns_name_fromregion(&name, ®ion); + RETERR(dns_name_towire(&name, cctx, target)); + isc_region_consume(®ion, name_length(&name)); + + /* + * SvcParams. + */ + return (mem_tobuffer(target, region.base, region.length)); +} + +static inline isc_result_t +towire_in_svcb(ARGS_TOWIRE) { + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(rdata->length != 0); + + return (generic_towire_in_svcb(CALL_TOWIRE)); +} + +static inline int +compare_in_svcb(ARGS_COMPARE) { + isc_region_t region1; + isc_region_t region2; + + REQUIRE(rdata1->type == rdata2->type); + REQUIRE(rdata1->rdclass == rdata2->rdclass); + REQUIRE(rdata1->type == dns_rdatatype_svcb); + REQUIRE(rdata1->rdclass == dns_rdataclass_in); + REQUIRE(rdata1->length != 0); + REQUIRE(rdata2->length != 0); + + dns_rdata_toregion(rdata1, ®ion1); + dns_rdata_toregion(rdata2, ®ion2); + + return (isc_region_compare(®ion1, ®ion2)); +} + +static inline isc_result_t +generic_fromstruct_in_svcb(ARGS_FROMSTRUCT) { + dns_rdata_in_svcb_t *svcb = source; + isc_region_t region; + + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdtype == type); + REQUIRE(svcb->common.rdclass == rdclass); + + UNUSED(type); + UNUSED(rdclass); + + RETERR(uint16_tobuffer(svcb->priority, target)); + dns_name_toregion(&svcb->svcdomain, ®ion); + RETERR(isc_buffer_copyregion(target, ®ion)); + + return (mem_tobuffer(target, svcb->svc, svcb->svclen)); +} + +static inline isc_result_t +fromstruct_in_svcb(ARGS_FROMSTRUCT) { + dns_rdata_in_svcb_t *svcb = source; + + REQUIRE(type == dns_rdatatype_svcb); + REQUIRE(rdclass == dns_rdataclass_in); + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdtype == type); + REQUIRE(svcb->common.rdclass == rdclass); + + return (generic_fromstruct_in_svcb(CALL_FROMSTRUCT)); +} + +static inline isc_result_t +generic_tostruct_in_svcb(ARGS_TOSTRUCT) { + isc_region_t region; + dns_rdata_in_svcb_t *svcb = target; + dns_name_t name; + + REQUIRE(svcb != NULL); + REQUIRE(rdata->length != 0); + + svcb->common.rdclass = rdata->rdclass; + svcb->common.rdtype = rdata->type; + ISC_LINK_INIT(&svcb->common, link); + + dns_rdata_toregion(rdata, ®ion); + + svcb->priority = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + + dns_name_init(&svcb->svcdomain, NULL); + dns_name_init(&name, NULL); + dns_name_fromregion(&name, ®ion); + isc_region_consume(®ion, name_length(&name)); + + RETERR(name_duporclone(&name, mctx, &svcb->svcdomain)); + svcb->svclen = region.length; + svcb->svc = mem_maybedup(mctx, region.base, region.length); + + if (svcb->svc == NULL) { + if (mctx != NULL) { + dns_name_free(&svcb->svcdomain, svcb->mctx); + } + return (ISC_R_NOMEMORY); + } + + svcb->offset = 0; + svcb->mctx = mctx; + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +tostruct_in_svcb(ARGS_TOSTRUCT) { + dns_rdata_in_svcb_t *svcb = target; + + REQUIRE(rdata->rdclass == dns_rdataclass_in); + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(svcb != NULL); + REQUIRE(rdata->length != 0); + + return (generic_tostruct_in_svcb(CALL_TOSTRUCT)); +} + +static inline void +generic_freestruct_in_svcb(ARGS_FREESTRUCT) { + dns_rdata_in_svcb_t *svcb = source; + + REQUIRE(svcb != NULL); + + if (svcb->mctx == NULL) { + return; + } + + dns_name_free(&svcb->svcdomain, svcb->mctx); + isc_mem_free(svcb->mctx, svcb->svc); + svcb->mctx = NULL; +} + +static inline void +freestruct_in_svcb(ARGS_FREESTRUCT) { + dns_rdata_in_svcb_t *svcb = source; + + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdclass == dns_rdataclass_in); + REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb); + + generic_freestruct_in_svcb(CALL_FREESTRUCT); +} + +static inline isc_result_t +generic_additionaldata_in_svcb(ARGS_ADDLDATA) { + bool alias, done = false; + dns_fixedname_t fixed; + dns_name_t name, *fname = NULL; + dns_offsets_t offsets; + dns_rdataset_t rdataset; + isc_region_t region; + unsigned int cnames = 0; + + dns_name_init(&name, offsets); + dns_rdata_toregion(rdata, ®ion); + alias = uint16_fromregion(®ion) == 0; + isc_region_consume(®ion, 2); + + dns_name_fromregion(&name, ®ion); + + if (dns_name_equal(&name, dns_rootname)) { + /* + * "." only means owner name in service form. + */ + if (alias || dns_name_equal(owner, dns_rootname) || + !dns_name_ishostname(owner, false)) + { + return (ISC_R_SUCCESS); + } + /* Only lookup address records */ + return ((add)(arg, owner, dns_rdatatype_a, NULL)); + } + + /* + * Follow CNAME chains when processing HTTPS and SVCB records. + */ + dns_rdataset_init(&rdataset); + fname = dns_fixedname_initname(&fixed); + do { + RETERR((add)(arg, &name, dns_rdatatype_cname, &rdataset)); + if (dns_rdataset_isassociated(&rdataset)) { + isc_result_t result; + result = dns_rdataset_first(&rdataset); + if (result == ISC_R_SUCCESS) { + dns_rdata_t current = DNS_RDATA_INIT; + dns_rdata_cname_t cname; + + dns_rdataset_current(&rdataset, ¤t); + + result = dns_rdata_tostruct(¤t, &cname, + NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_name_copy(&cname.cname, fname); + dns_name_clone(fname, &name); + } else { + done = true; + } + dns_rdataset_disassociate(&rdataset); + } else { + done = true; + } + /* + * Stop following a potentially infinite CNAME chain. + */ + if (!done && cnames++ > MAX_CNAMES) { + return (ISC_R_SUCCESS); + } + } while (!done); + + /* + * Look up HTTPS/SVCB records when processing the alias form. + */ + if (alias) { + RETERR((add)(arg, &name, rdata->type, &rdataset)); + /* + * Don't return A or AAAA if this is not the last element + * in the HTTP / SVCB chain. + */ + if (dns_rdataset_isassociated(&rdataset)) { + dns_rdataset_disassociate(&rdataset); + return (ISC_R_SUCCESS); + } + } + return ((add)(arg, &name, dns_rdatatype_a, NULL)); +} + +static inline isc_result_t +additionaldata_in_svcb(ARGS_ADDLDATA) { + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + return (generic_additionaldata_in_svcb(CALL_ADDLDATA)); +} + +static inline isc_result_t +digest_in_svcb(ARGS_DIGEST) { + isc_region_t region1; + + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + dns_rdata_toregion(rdata, ®ion1); + return ((digest)(arg, ®ion1)); +} + +static inline bool +checkowner_in_svcb(ARGS_CHECKOWNER) { + REQUIRE(type == dns_rdatatype_svcb); + REQUIRE(rdclass == dns_rdataclass_in); + + UNUSED(name); + UNUSED(type); + UNUSED(rdclass); + UNUSED(wildcard); + + return (true); +} + +static inline bool +generic_checknames_in_svcb(ARGS_CHECKNAMES) { + isc_region_t region; + dns_name_t name; + bool alias; + + UNUSED(owner); + + dns_rdata_toregion(rdata, ®ion); + INSIST(region.length > 1); + alias = uint16_fromregion(®ion) == 0; + isc_region_consume(®ion, 2); + dns_name_init(&name, NULL); + dns_name_fromregion(&name, ®ion); + if (!alias && !dns_name_ishostname(&name, false)) { + if (bad != NULL) { + dns_name_clone(&name, bad); + } + return (false); + } + return (true); +} + +static inline bool +checknames_in_svcb(ARGS_CHECKNAMES) { + REQUIRE(rdata->type == dns_rdatatype_svcb); + REQUIRE(rdata->rdclass == dns_rdataclass_in); + + return (generic_checknames_in_svcb(CALL_CHECKNAMES)); +} + +static inline int +casecompare_in_svcb(ARGS_COMPARE) { + return (compare_in_svcb(rdata1, rdata2)); +} + +static isc_result_t +generic_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) { + if (svcb->svclen == 0) { + return (ISC_R_NOMORE); + } + svcb->offset = 0; + return (ISC_R_SUCCESS); +} + +static isc_result_t +generic_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) { + isc_region_t region; + size_t len; + + if (svcb->offset >= svcb->svclen) { + return (ISC_R_NOMORE); + } + + region.base = svcb->svc + svcb->offset; + region.length = svcb->svclen - svcb->offset; + INSIST(region.length >= 4); + isc_region_consume(®ion, 2); + len = uint16_fromregion(®ion); + INSIST(region.length >= len + 2); + svcb->offset += len + 4; + return (svcb->offset >= svcb->svclen ? ISC_R_NOMORE : ISC_R_SUCCESS); +} + +static void +generic_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) { + size_t len; + + INSIST(svcb->offset <= svcb->svclen); + + region->base = svcb->svc + svcb->offset; + region->length = svcb->svclen - svcb->offset; + INSIST(region->length >= 4); + isc_region_consume(region, 2); + len = uint16_fromregion(region); + INSIST(region->length >= len + 2); + region->base = svcb->svc + svcb->offset; + region->length = len + 4; +} + +isc_result_t +dns_rdata_in_svcb_first(dns_rdata_in_svcb_t *svcb) { + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb); + REQUIRE(svcb->common.rdclass == dns_rdataclass_in); + + return (generic_rdata_in_svcb_first(svcb)); +} + +isc_result_t +dns_rdata_in_svcb_next(dns_rdata_in_svcb_t *svcb) { + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb); + REQUIRE(svcb->common.rdclass == dns_rdataclass_in); + + return (generic_rdata_in_svcb_next(svcb)); +} + +void +dns_rdata_in_svcb_current(dns_rdata_in_svcb_t *svcb, isc_region_t *region) { + REQUIRE(svcb != NULL); + REQUIRE(svcb->common.rdtype == dns_rdatatype_svcb); + REQUIRE(svcb->common.rdclass == dns_rdataclass_in); + REQUIRE(region != NULL); + + generic_rdata_in_svcb_current(svcb, region); +} + +#endif /* RDATA_IN_1_SVCB_64_C */ diff --git a/lib/dns/rdata/in_1/svcb_64.h b/lib/dns/rdata/in_1/svcb_64.h new file mode 100644 index 0000000000..750b6820da --- /dev/null +++ b/lib/dns/rdata/in_1/svcb_64.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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. + */ + +#ifndef IN_1_SVCB_64_H +#define IN_1_SVCB_64_H 1 + +/*! + * \brief Per draft-ietf-dnsop-svcb-https-02 + */ + +typedef struct dns_rdata_in_svcb { + dns_rdatacommon_t common; + isc_mem_t *mctx; + uint16_t priority; + dns_name_t svcdomain; + unsigned char *svc; + uint16_t svclen; + uint16_t offset; +} dns_rdata_in_svcb_t; + +isc_result_t +dns_rdata_in_svcb_first(dns_rdata_in_svcb_t *); + +isc_result_t +dns_rdata_in_svcb_next(dns_rdata_in_svcb_t *); + +void +dns_rdata_in_svcb_current(dns_rdata_in_svcb_t *, isc_region_t *); + +#endif /* IN_1_SVCB_64_H */ diff --git a/lib/dns/rdata/in_1/wks_11.c b/lib/dns/rdata/in_1/wks_11.c index caa3892b64..b837c55920 100644 --- a/lib/dns/rdata/in_1/wks_11.c +++ b/lib/dns/rdata/in_1/wks_11.c @@ -360,13 +360,14 @@ freestruct_in_wks(ARGS_FREESTRUCT) { static inline isc_result_t additionaldata_in_wks(ARGS_ADDLDATA) { - UNUSED(rdata); - UNUSED(add); - UNUSED(arg); - REQUIRE(rdata->type == dns_rdatatype_wks); REQUIRE(rdata->rdclass == dns_rdataclass_in); + UNUSED(rdata); + UNUSED(owner); + UNUSED(add); + UNUSED(arg); + return (ISC_R_SUCCESS); } diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index 87fda079f9..c51916e7dd 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -576,6 +576,7 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name, isc_result_t dns_rdataset_additionaldata(dns_rdataset_t *rdataset, + const dns_name_t *owner_name, dns_additionaldatafunc_t add, void *arg) { dns_rdata_t rdata = DNS_RDATA_INIT; isc_result_t result; @@ -595,7 +596,7 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset, do { dns_rdataset_current(rdataset, &rdata); - result = dns_rdata_additionaldata(&rdata, add, arg); + result = dns_rdata_additionaldata(&rdata, owner_name, add, arg); if (result == ISC_R_SUCCESS) { result = dns_rdataset_next(rdataset); } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index f8b9c4d9a9..0358241d95 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -7026,7 +7026,7 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, bool external, static isc_result_t check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type, - dns_section_t section) { + dns_rdataset_t *found, dns_section_t section) { respctx_t *rctx = arg; fetchctx_t *fctx = rctx->fctx; isc_result_t result; @@ -7071,6 +7071,9 @@ check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type, result = dns_message_findtype(name, type, 0, &rdataset); if (result == ISC_R_SUCCESS) { mark_related(name, rdataset, external, gluing); + if (found != NULL) { + dns_rdataset_clone(rdataset, found); + } /* * Do we have its SIG too? */ @@ -7090,8 +7093,10 @@ check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type, } static isc_result_t -check_related(void *arg, const dns_name_t *addname, dns_rdatatype_t type) { - return (check_section(arg, addname, type, DNS_SECTION_ADDITIONAL)); +check_related(void *arg, const dns_name_t *addname, dns_rdatatype_t type, + dns_rdataset_t *found) { + return (check_section(arg, addname, type, found, + DNS_SECTION_ADDITIONAL)); } #ifndef CHECK_FOR_GLUE_IN_ANSWER @@ -7100,8 +7105,9 @@ check_related(void *arg, const dns_name_t *addname, dns_rdatatype_t type) { #if CHECK_FOR_GLUE_IN_ANSWER static isc_result_t -check_answer(void *arg, const dns_name_t *addname, dns_rdatatype_t type) { - return (check_section(arg, addname, type, DNS_SECTION_ANSWER)); +check_answer(void *arg, const dns_name_t *addname, dns_rdatatype_t type, + dns_rdataset_t *found) { + return (check_section(arg, addname, type, found, DNS_SECTION_ANSWER)); } #endif /* if CHECK_FOR_GLUE_IN_ANSWER */ @@ -8774,8 +8780,8 @@ rctx_answer_any(respctx_t *rctx) { rdataset->attributes |= DNS_RDATASETATTR_CACHE; rdataset->trust = rctx->trust; - (void)dns_rdataset_additionaldata(rdataset, check_related, - rctx); + (void)dns_rdataset_additionaldata(rdataset, rctx->aname, + check_related, rctx); } return (ISC_R_SUCCESS); @@ -8822,7 +8828,8 @@ rctx_answer_match(respctx_t *rctx) { rctx->ardataset->attributes |= DNS_RDATASETATTR_ANSWER; rctx->ardataset->attributes |= DNS_RDATASETATTR_CACHE; rctx->ardataset->trust = rctx->trust; - (void)dns_rdataset_additionaldata(rctx->ardataset, check_related, rctx); + (void)dns_rdataset_additionaldata(rctx->ardataset, rctx->aname, + check_related, rctx); for (sigrdataset = ISC_LIST_HEAD(rctx->aname->list); sigrdataset != NULL; @@ -9030,7 +9037,8 @@ rctx_authority_positive(respctx_t *rctx) { * to this rdataset. */ (void)dns_rdataset_additionaldata( - rdataset, check_related, rctx); + rdataset, name, check_related, + rctx); done = true; } } @@ -9531,8 +9539,8 @@ rctx_referral(respctx_t *rctx) { */ INSIST(rctx->ns_rdataset != NULL); FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING); - (void)dns_rdataset_additionaldata(rctx->ns_rdataset, check_related, - rctx); + (void)dns_rdataset_additionaldata(rctx->ns_rdataset, rctx->ns_name, + check_related, rctx); #if CHECK_FOR_GLUE_IN_ANSWER /* * Look in the answer section for "glue" that is incorrectly @@ -9544,8 +9552,8 @@ rctx_referral(respctx_t *rctx) { if (rctx->glue_in_answer && (fctx->type == dns_rdatatype_aaaa || fctx->type == dns_rdatatype_a)) { - (void)dns_rdataset_additionaldata(rctx->ns_rdataset, - check_answer, fctx); + (void)dns_rdataset_additionaldata( + rctx->ns_rdataset, rctx->ns_name, check_answer, fctx); } #endif /* if CHECK_FOR_GLUE_IN_ANSWER */ FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING); @@ -9655,7 +9663,7 @@ again: if (CHASE(rdataset)) { rdataset->attributes &= ~DNS_RDATASETATTR_CHASE; (void)dns_rdataset_additionaldata( - rdataset, check_related, rctx); + rdataset, name, check_related, rctx); rescan = true; } } diff --git a/lib/dns/result.c b/lib/dns/result.c index 1c8641227b..01ce2b4e85 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -174,6 +174,7 @@ static const char *text[DNS_R_NRESULTS] = { "cannot use NSEC3 with key algorithm", /*%< 125 DNS_R_NSEC3BADALG */ "NSEC3 resalt", /*%< 126 DNS_R_NSEC3RESALT */ + "inconsistent resource record", /*%< 127 DNS_R_INCONSISTENTRR */ }; static const char *ids[DNS_R_NRESULTS] = { @@ -308,6 +309,7 @@ static const char *ids[DNS_R_NRESULTS] = { "DNS_R_NSEC3SALTRANGE", "DNS_R_NSEC3BADALG", "DNS_R_NSEC3RESALT", + "DNS_R_INCONSISTENTRR", }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/tests/rdata_test.c b/lib/dns/tests/rdata_test.c index 131f7804a7..5d0fc9cb07 100644 --- a/lib/dns/tests/rdata_test.c +++ b/lib/dns/tests/rdata_test.c @@ -48,6 +48,12 @@ struct compare_ok { }; typedef struct compare_ok compare_ok_t; +struct textvsunknown { + const char *text1; + const char *text2; +}; +typedef struct textvsunknown textvsunknown_t; + static int _setup(void **state) { isc_result_t result; @@ -201,10 +207,12 @@ rdata_towire(dns_rdata_t *rdata, unsigned char *dst, size_t dstlen, } static isc_result_t -additionaldata_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { +additionaldata_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, + dns_rdataset_t *found) { UNUSED(arg); UNUSED(name); UNUSED(qtype); + UNUSED(found); return (ISC_R_SUCCESS); } @@ -213,7 +221,8 @@ additionaldata_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { */ static isc_result_t rdata_additionadata(dns_rdata_t *rdata) { - return (dns_rdata_additionaldata(rdata, additionaldata_cb, NULL)); + return (dns_rdata_additionaldata(rdata, dns_rootname, additionaldata_cb, + NULL)); } /* @@ -299,7 +308,8 @@ check_struct_conversions(dns_rdata_t *rdata, size_t structsize, assert_memory_equal(buf, rdata->data, rdata->length); /* - * Check that one can walk hip rendezvous servers. + * Check that one can walk hip rendezvous servers and + * https/svcb parameters. */ switch (type) { case dns_rdatatype_hip: { @@ -319,6 +329,38 @@ check_struct_conversions(dns_rdata_t *rdata, size_t structsize, assert_int_equal(count, loop); break; } + case dns_rdatatype_https: { + dns_rdata_in_https_t *https = rdata_struct; + + for (result = dns_rdata_in_https_first(https); + result == ISC_R_SUCCESS; + result = dns_rdata_in_https_next(https)) + { + isc_region_t region; + dns_rdata_in_https_current(https, ®ion); + assert_true(region.length >= 4); + count++; + } + assert_int_equal(result, ISC_R_NOMORE); + assert_int_equal(count, loop); + break; + } + case dns_rdatatype_svcb: { + dns_rdata_in_svcb_t *svcb = rdata_struct; + + for (result = dns_rdata_in_svcb_first(svcb); + result == ISC_R_SUCCESS; + result = dns_rdata_in_svcb_next(svcb)) + { + isc_region_t region; + dns_rdata_in_svcb_current(svcb, ®ion); + assert_true(region.length >= 4); + count++; + } + assert_int_equal(result, ISC_R_NOMORE); + assert_int_equal(count, loop); + break; + } } isc_mem_free(dt_mctx, rdata_struct); @@ -339,6 +381,10 @@ check_text_ok_single(const text_ok_t *text_ok, dns_rdataclass_t rdclass, isc_result_t result; size_t length = 0; + if (debug) { + fprintf(stdout, "#check_text_ok_single(%s)\n", + text_ok->text_in); + } /* * Try converting text form RDATA into uncompressed wire form. */ @@ -350,7 +396,9 @@ check_text_ok_single(const text_ok_t *text_ok, dns_rdataclass_t rdclass, */ if (text_ok->text_out != NULL) { if (debug && result != ISC_R_SUCCESS) { - fprintf(stdout, "#'%s'\n", text_ok->text_in); + fprintf(stdout, "# '%s'\n", text_ok->text_in); + fprintf(stdout, "# result=%s\n", + dns_result_totext(result)); } assert_int_equal(result, ISC_R_SUCCESS); } else { @@ -374,6 +422,18 @@ check_text_ok_single(const text_ok_t *text_ok, dns_rdataclass_t rdclass, */ isc_buffer_init(&target, buf_totext, sizeof(buf_totext)); result = dns_rdata_totext(&rdata, NULL, &target); + if (result != ISC_R_SUCCESS && debug) { + size_t i; + fprintf(stdout, "# dns_rdata_totext -> %s", + dns_result_totext(result)); + for (i = 0; i < rdata.length; i++) { + if ((i % 16) == 0) { + fprintf(stdout, "\n#"); + } + fprintf(stdout, " %02x", rdata.data[i]); + } + fprintf(stdout, "\n"); + } assert_int_equal(result, ISC_R_SUCCESS); /* * Ensure buf_totext is properly NUL terminated as dns_rdata_totext() @@ -387,6 +447,10 @@ check_text_ok_single(const text_ok_t *text_ok, dns_rdataclass_t rdclass, } assert_string_equal(buf_totext, text_ok->text_out); + if (debug) { + fprintf(stdout, "#dns_rdata_totext -> '%s'\n", buf_totext); + } + /* * Ensure that fromtext_*() output is valid input for fromwire_*(). */ @@ -463,6 +527,10 @@ check_text_conversions(dns_rdata_t *rdata) { result = dns_test_rdatafromstring(&rdata2, rdata->rdclass, rdata->type, buf_fromtext, sizeof(buf_fromtext), buf_totext, false); + if (debug && result != ISC_R_SUCCESS) { + fprintf(stdout, "# result = %s\n", dns_result_totext(result)); + fprintf(stdout, "# '%s'\n", buf_fromtext); + } assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(rdata2.length, rdata->length); assert_memory_equal(buf_fromtext, rdata->data, rdata->length); @@ -726,6 +794,64 @@ check_rdata(const text_ok_t *text_ok, const wire_ok_t *wire_ok, } } +/* + * Check presentation vs unknown format of the record. + */ +static void +check_textvsunknown_single(const textvsunknown_t *textvsunknown, + dns_rdataclass_t rdclass, dns_rdatatype_t type) { + dns_rdata_t rdata1 = DNS_RDATA_INIT, rdata2 = DNS_RDATA_INIT; + unsigned char buf1[1024], buf2[1024]; + isc_result_t result; + + result = dns_test_rdatafromstring(&rdata1, rdclass, type, buf1, + sizeof(buf1), textvsunknown->text1, + false); + if (debug && result != ISC_R_SUCCESS) { + fprintf(stdout, "# '%s'\n", textvsunknown->text1); + fprintf(stdout, "# result=%s\n", dns_result_totext(result)); + } + assert_int_equal(result, ISC_R_SUCCESS); + result = dns_test_rdatafromstring(&rdata2, rdclass, type, buf2, + sizeof(buf2), textvsunknown->text2, + false); + if (debug && result != ISC_R_SUCCESS) { + fprintf(stdout, "# '%s'\n", textvsunknown->text2); + fprintf(stdout, "# result=%s\n", dns_result_totext(result)); + } + assert_int_equal(result, ISC_R_SUCCESS); + if (debug && rdata1.length != rdata2.length) { + fprintf(stdout, "# '%s'\n", textvsunknown->text1); + fprintf(stdout, "# rdata1.length (%u) != rdata2.length (%u)\n", + rdata1.length, rdata2.length); + } + assert_int_equal(rdata1.length, rdata2.length); + if (debug && memcmp(rdata1.data, rdata2.data, rdata1.length) != 0) { + unsigned int i; + fprintf(stdout, "# '%s'\n", textvsunknown->text1); + for (i = 0; i < rdata1.length; i++) { + if (rdata1.data[i] != rdata2.data[i]) { + fprintf(stderr, "# %u: %02x != %02x\n", i, + rdata1.data[i], rdata2.data[i]); + } + } + } + assert_memory_equal(rdata1.data, rdata2.data, rdata1.length); +} + +static void +check_textvsunknown(const textvsunknown_t *textvsunknown, + dns_rdataclass_t rdclass, dns_rdatatype_t type) { + size_t i; + + /* + * Check all entries in the supplied array. + */ + for (i = 0; textvsunknown[i].text1 != NULL; i++) { + check_textvsunknown_single(&textvsunknown[i], rdclass, type); + } +} + /* * Common tests for RR types based on KEY that require key data: * @@ -2412,6 +2538,255 @@ wks(void **state) { dns_rdatatype_wks, sizeof(dns_rdata_in_wks_t)); } +static void +https_svcb(void **state) { + /* + * Known keys: mandatory, apln, no-default-alpn, port, + * ipv4hint, port, ipv6hint. + */ + text_ok_t text_ok[] = { + /* unknown key invalid */ + TEXT_INVALID("1 . unknown="), + /* no domain */ + TEXT_INVALID("0"), + /* minimal record */ + TEXT_VALID_LOOP(0, "0 ."), + /* Alias form requires SvcFieldValue to be empty */ + TEXT_INVALID("0 . alpn=\"h2\""), + /* no "key" prefix */ + TEXT_INVALID("2 svc.example.net. 0=\"2222\""), + /* no key value */ + TEXT_INVALID("2 svc.example.net. key"), + /* no key value */ + TEXT_INVALID("2 svc.example.net. key=\"2222\""), + /* zero pad invalid */ + TEXT_INVALID("2 svc.example.net. key07=\"2222\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. key7=\"2222\""), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. key7=2222", + "2 svc.example.net. key7=\"2222\""), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h2", + "2 svc.example.net. alpn=\"h2\""), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h3", + "2 svc.example.net. alpn=\"h3\""), + /* alpn has 2 sub field "h2" and "h3" */ + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h2,h3", + "2 svc.example.net. alpn=\"h2,h3\""), + /* apln has 2 sub fields "h1,h2" and "h3" (comma escaped) */ + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. alpn=h1\\\\,h2,h3", + "2 svc.example.net. alpn=\"h1\\\\,h2,h3\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. port=50"), + /* no-default-alpn, alpn is required */ + TEXT_INVALID("2 svc.example.net. no-default-alpn"), + /* no-default-alpn with alpn present */ + TEXT_VALID_LOOPCHG( + 2, "2 svc.example.net. no-default-alpn alpn=h2", + "2 svc.example.net. alpn=\"h2\" no-default-alpn"), + /* empty hint */ + TEXT_INVALID("2 svc.example.net. ipv4hint="), + TEXT_VALID_LOOP(1, "2 svc.example.net. " + "ipv4hint=10.50.0.1,10.50.0.2"), + /* empty hint */ + TEXT_INVALID("2 svc.example.net. ipv6hint="), + TEXT_VALID_LOOP(1, "2 svc.example.net. ipv6hint=::1,2002::1"), + TEXT_VALID_LOOP(1, "2 svc.example.net. ech=abcdefghijkl"), + /* bad base64 */ + TEXT_INVALID("2 svc.example.net. ech=abcdefghijklm"), + TEXT_VALID_LOOP(1, "2 svc.example.net. key7=\"2222\""), + /* Out of key order on input (alpn == key1). */ + TEXT_VALID_LOOPCHG(2, + "2 svc.example.net. key7=\"2222\" alpn=h2", + "2 svc.example.net. alpn=\"h2\" " + "key7=\"2222\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. key65535=\"2222\""), + TEXT_INVALID("2 svc.example.net. key65536=\"2222\""), + TEXT_VALID_LOOP(1, "2 svc.example.net. key10"), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. key11=", + "2 svc.example.net. key11"), + TEXT_VALID_LOOPCHG(1, "2 svc.example.net. key12=\"\"", + "2 svc.example.net. key12"), + /* empty alpn-id sub fields */ + TEXT_INVALID("2 svc.example.net. alpn"), + TEXT_INVALID("2 svc.example.net. alpn="), + TEXT_INVALID("2 svc.example.net. alpn=,h1"), + TEXT_INVALID("2 svc.example.net. alpn=h1,"), + TEXT_INVALID("2 svc.example.net. alpn=h1,,h2"), + /* mandatory */ + TEXT_VALID_LOOP(2, "2 svc.example.net. mandatory=alpn " + "alpn=\"h2\""), + TEXT_VALID_LOOP(3, "2 svc.example.net. mandatory=alpn,port " + "alpn=\"h2\" port=443"), + TEXT_VALID_LOOPCHG(3, + "2 svc.example.net. mandatory=port,alpn " + "alpn=\"h2\" port=443", + "2 svc.example.net. mandatory=alpn,port " + "alpn=\"h2\" port=443"), + TEXT_INVALID("2 svc.example.net. mandatory=mandatory"), + TEXT_INVALID("2 svc.example.net. mandatory=port"), + TEXT_INVALID("2 svc.example.net. mandatory=,port port=433"), + TEXT_INVALID("2 svc.example.net. mandatory=port, port=433"), + TEXT_INVALID("2 svc.example.net. " + "mandatory=alpn,,port alpn=h2 port=433"), + /* mandatory w/ unknown key values */ + TEXT_VALID_LOOP(2, "2 svc.example.net. mandatory=key7 key7"), + TEXT_VALID_LOOP(3, "2 svc.example.net. mandatory=key7,key8 " + "key7 key8"), + TEXT_VALID_LOOPCHG( + 3, "2 svc.example.net. mandatory=key8,key7 key7 key8", + "2 svc.example.net. mandatory=key7,key8 key7 key8"), + TEXT_INVALID("2 svc.example.net. " + "mandatory=key7,key7"), + TEXT_INVALID("2 svc.example.net. mandatory=,key7"), + TEXT_INVALID("2 svc.example.net. mandatory=key7,"), + TEXT_INVALID("2 svc.example.net. " + "mandatory=key7,,key7"), + /* Invalid test vectors */ + TEXT_INVALID("1 foo.example.com. ( key123=abc key123=def )"), + TEXT_INVALID("1 foo.example.com. mandatory"), + TEXT_INVALID("1 foo.example.com. alpn"), + TEXT_INVALID("1 foo.example.com. port"), + TEXT_INVALID("1 foo.example.com. ipv4hint"), + TEXT_INVALID("1 foo.example.com. ipv6hint"), + TEXT_INVALID("1 foo.example.com. no-default-alpn=abc"), + TEXT_INVALID("1 foo.example.com. mandatory=key123"), + TEXT_INVALID("1 foo.example.com. mandatory=mandatory"), + TEXT_INVALID("1 foo.example.com. ( mandatory=key123,key123 " + "key123=abc)"), + TEXT_SENTINEL() + + }; + wire_ok_t wire_ok[] = { + /* + * Too short + */ + WIRE_INVALID(0x00, 0x00), + /* + * Minimal length record. + */ + WIRE_VALID(0x00, 0x00, 0x00), + /* + * Alias with non-empty SvcFieldValue (key7=""). + */ + WIRE_INVALID(0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00), + /* + * Bad key7= length (longer than rdata). + */ + WIRE_INVALID(0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x01), + /* + * Port (0x03) too small (zero and one octets). + */ + WIRE_INVALID(0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00), + WIRE_INVALID(0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00), + /* Valid port */ + WIRE_VALID_LOOP(1, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x00), + /* + * Port (0x03) too big (three octets). + */ + WIRE_INVALID(0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x00), + /* + * Duplicate keys. + */ + WIRE_INVALID(0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00), + /* + * Out of order keys. + */ + WIRE_INVALID(0x01, 0x01, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00), + /* + * Empty of mandatory key list. + */ + WIRE_INVALID(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00), + /* + * "mandatory=mandatory" is invalid + */ + WIRE_INVALID(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00), + /* + * Out of order mandatory key list. + */ + WIRE_INVALID(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x80, 0x00, 0x71, 0x00, 0x71, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00), + /* + * Alpn(0x00 0x01) (length 0x00 0x09) "h1,h2" + "h3" + */ + WIRE_VALID_LOOP(0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x09, + 5, 'h', '1', ',', 'h', '2', 2, 'h', '3'), + /* + * Alpn(0x00 0x01) (length 0x00 0x09) "h1\h2" + "h3" + */ + WIRE_VALID_LOOP(0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x09, + 5, 'h', '1', '\\', 'h', '2', 2, 'h', '3'), + WIRE_SENTINEL() + }; + /* Test vectors from RFCXXXX */ + textvsunknown_t textvsunknown[] = { + /* AliasForm */ + { "0 foo.example.com", "\\# 19 ( 00 00 03 66 6f 6f 07 65 78 61 " + "6d 70 6c 65 03 63 6f 6d 00)" }, + /* ServiceForm */ + { "1 .", "\\# 3 ( 00 01 00)" }, + /* Port example */ + { "16 foo.example.com port=53", + "\\# 25 ( 00 10 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f " + "6d 00 00 03 00 02 00 35 )" }, + /* Unregistered keys with unquoted value. */ + { "1 foo.example.com key667=hello", + "\\# 28 ( 00 01 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f " + "6d 00 02 9b 00 05 68 65 6c 6c 6f )" }, + /* + * Quoted decimal-escaped character. + * 1 foo.example.com key667="hello\210qoo" + */ + { "1 foo.example.com key667=\"hello\\210qoo\"", + "\\# 32 ( 00 01 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f " + "6d 00 02 9b 00 09 68 65 6c 6c 6f d2 71 6f 6f )" }, + /* + * IPv6 hints example, quoted. + * 1 foo.example.com ipv6hint="2001:db8::1,2001:db8::53:1" + */ + { "1 foo.example.com ipv6hint=\"2001:db8::1,2001:db8::53:1\"", + "\\# 55 ( 00 01 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 63 6f " + "6d 00 00 06 00 20 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 " + "00 01 20 01 0d b8 00 00 00 00 00 00 00 00 00 53 00 01 )" }, + /* SvcParamValues and mandatory out of order. */ + { "16 foo.example.org alpn=h2,h3-19 mandatory=ipv4hint,alpn " + "ipv4hint=192.0.2.1", + "\\# 48 ( 00 10 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 6f 72 " + "67 00 00 00 00 04 00 01 00 04 00 01 00 09 02 68 32 05 68 33 " + "2d 31 39 00 04 00 04 c0 00 02 01 )" }, + /* + * Quoted ALPN with escaped comma and backslash. + * 16 foo.example.org alpn="f\\\\oo\\,bar,h2" + */ + { "16 foo.example.org alpn=\"f\\\\\\\\oo\\\\,bar,h2\"", + "\\# 35 ( 00 10 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 6f 72 " + "67 00 00 01 00 0c 08 66 5c 6f 6f 2c 62 61 72 02 68 32 )" }, + /* + * Unquoted ALPN with escaped comma and backslash. + * 16 foo.example.org alpn=f\\\092oo\092,bar,h2 + */ + { "16 foo.example.org alpn=f\\\\\\092oo\\092,bar,h2", + "\\# 35 ( 00 10 03 66 6f 6f 07 65 78 61 6d 70 6c 65 03 6f 72 " + "67 00 00 01 00 0c 08 66 5c 6f 6f 2c 62 61 72 02 68 32 )" }, + { NULL, NULL } + }; + + UNUSED(state); + + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_svcb, sizeof(dns_rdata_in_svcb_t)); + check_rdata(text_ok, wire_ok, NULL, false, dns_rdataclass_in, + dns_rdatatype_https, sizeof(dns_rdata_in_https_t)); + + check_textvsunknown(textvsunknown, dns_rdataclass_in, + dns_rdatatype_svcb); + check_textvsunknown(textvsunknown, dns_rdataclass_in, + dns_rdatatype_https); +} + /* * ZONEMD tests. * @@ -2755,18 +3130,18 @@ iszonecutauth(void **state) { int main(int argc, char **argv) { const struct CMUnitTest tests[] = { + /* types */ cmocka_unit_test_setup_teardown(amtrelay, _setup, _teardown), cmocka_unit_test_setup_teardown(apl, _setup, _teardown), cmocka_unit_test_setup_teardown(atma, _setup, _teardown), cmocka_unit_test_setup_teardown(cdnskey, _setup, _teardown), cmocka_unit_test_setup_teardown(csync, _setup, _teardown), - cmocka_unit_test_setup_teardown(doa, _setup, _teardown), cmocka_unit_test_setup_teardown(dnskey, _setup, _teardown), + cmocka_unit_test_setup_teardown(doa, _setup, _teardown), cmocka_unit_test_setup_teardown(ds, _setup, _teardown), cmocka_unit_test_setup_teardown(eid, _setup, _teardown), - cmocka_unit_test_setup_teardown(edns_client_subnet, _setup, - _teardown), cmocka_unit_test_setup_teardown(hip, _setup, _teardown), + cmocka_unit_test_setup_teardown(https_svcb, _setup, _teardown), cmocka_unit_test_setup_teardown(isdn, _setup, _teardown), cmocka_unit_test_setup_teardown(key, _setup, _teardown), cmocka_unit_test_setup_teardown(loc, _setup, _teardown), @@ -2774,10 +3149,13 @@ main(int argc, char **argv) { cmocka_unit_test_setup_teardown(nsec, _setup, _teardown), cmocka_unit_test_setup_teardown(nsec3, _setup, _teardown), cmocka_unit_test_setup_teardown(nxt, _setup, _teardown), + cmocka_unit_test_setup_teardown(rkey, _setup, _teardown), cmocka_unit_test_setup_teardown(sshfp, _setup, _teardown), cmocka_unit_test_setup_teardown(wks, _setup, _teardown), - cmocka_unit_test_setup_teardown(rkey, _setup, _teardown), cmocka_unit_test_setup_teardown(zonemd, _setup, _teardown), + /* other tests */ + cmocka_unit_test_setup_teardown(edns_client_subnet, _setup, + _teardown), cmocka_unit_test_setup_teardown(atcname, NULL, NULL), cmocka_unit_test_setup_teardown(atparent, NULL, NULL), cmocka_unit_test_setup_teardown(iszonecutauth, NULL, NULL), diff --git a/lib/isc/include/isc/lex.h b/lib/isc/include/isc/lex.h index fdbe10b92a..84d4f42486 100644 --- a/lib/isc/include/isc/lex.h +++ b/lib/isc/include/isc/lex.h @@ -61,11 +61,11 @@ ISC_LANG_BEGINDECLS * Various options for isc_lex_gettoken(). */ -#define ISC_LEXOPT_EOL 0x01 /*%< Want end-of-line token. */ -#define ISC_LEXOPT_EOF 0x02 /*%< Want end-of-file token. */ -#define ISC_LEXOPT_INITIALWS 0x04 /*%< Want initial whitespace. */ -#define ISC_LEXOPT_NUMBER 0x08 /*%< Recognize numbers. */ -#define ISC_LEXOPT_QSTRING 0x10 /*%< Recognize qstrings. */ +#define ISC_LEXOPT_EOL 0x0001 /*%< Want end-of-line token. */ +#define ISC_LEXOPT_EOF 0x0002 /*%< Want end-of-file token. */ +#define ISC_LEXOPT_INITIALWS 0x0004 /*%< Want initial whitespace. */ +#define ISC_LEXOPT_NUMBER 0x0008 /*%< Recognize numbers. */ +#define ISC_LEXOPT_QSTRING 0x0010 /*%< Recognize qstrings. */ /*@}*/ /*@{*/ @@ -76,14 +76,16 @@ ISC_LANG_BEGINDECLS * the paren count is > 0. To use this option, '(' and ')' must be special * characters. */ -#define ISC_LEXOPT_DNSMULTILINE 0x20 /*%< Handle '(' and ')'. */ -#define ISC_LEXOPT_NOMORE 0x40 /*%< Want "no more" token. */ +#define ISC_LEXOPT_DNSMULTILINE 0x0020 /*%< Handle '(' and ')'. */ +#define ISC_LEXOPT_NOMORE 0x0040 /*%< Want "no more" token. */ -#define ISC_LEXOPT_CNUMBER 0x80 /*%< Recognize octal and hex. */ -#define ISC_LEXOPT_ESCAPE 0x100 /*%< Recognize escapes. */ -#define ISC_LEXOPT_QSTRINGMULTILINE 0x200 /*%< Allow multiline "" strings */ -#define ISC_LEXOPT_OCTAL 0x400 /*%< Expect a octal number. */ -#define ISC_LEXOPT_BTEXT 0x800 /*%< Bracketed text. */ +#define ISC_LEXOPT_CNUMBER 0x0080 /*%< Recognize octal and hex. */ +#define ISC_LEXOPT_ESCAPE 0x0100 /*%< Recognize escapes. */ +#define ISC_LEXOPT_QSTRINGMULTILINE 0x0200 /*%< Allow multiline "" strings */ +#define ISC_LEXOPT_OCTAL 0x0400 /*%< Expect a octal number. */ +#define ISC_LEXOPT_BTEXT 0x0800 /*%< Bracketed text. */ +#define ISC_LEXOPT_VPAIR 0x1000 /*%< Recognize value pair. */ +#define ISC_LEXOPT_QVPAIR 0x2000 /*%< Recognize quoted value pair. */ /*@}*/ /*@{*/ /*! @@ -117,7 +119,9 @@ typedef enum { isc_tokentype_initialws = 6, isc_tokentype_special = 7, isc_tokentype_nomore = 8, - isc_tokentype_btext = 8 + isc_tokentype_btext = 9, + isc_tokentype_vpair = 10, + isc_tokentype_qvpair = 11, } isc_tokentype_t; typedef union { diff --git a/lib/isc/lex.c b/lib/isc/lex.c index 3aef691694..8ab3682873 100644 --- a/lib/isc/lex.c +++ b/lib/isc/lex.c @@ -293,7 +293,10 @@ typedef enum { lexstate_ccommentend, lexstate_eatline, lexstate_qstring, - lexstate_btext + lexstate_btext, + lexstate_vpair, + lexstate_vpairstart, + lexstate_qvpair, } lexstate; #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL) @@ -663,6 +666,29 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { remaining--; break; case lexstate_string: + if (!escaped && c == '=' && + (options & ISC_LEXOPT_VPAIR) != 0) { + INSIST(remaining > 0U); + *curr++ = c; + *curr = '\0'; + remaining--; + state = lexstate_vpairstart; + break; + } + /* FALLTHROUGH */ + case lexstate_vpairstart: + if (state == lexstate_vpairstart) { + if (c == '"' && + (options & ISC_LEXOPT_QVPAIR) != 0) { + INSIST(remaining > 0U); + no_comments = true; + state = lexstate_qvpair; + break; + } + state = lexstate_vpair; + } + /* FALLTHROUGH */ + case lexstate_vpair: /* * EOF needs to be checked before lex->specials[c] * as lex->specials[EOF] is not a good idea. @@ -676,7 +702,13 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { result = source->result; goto done; } - tokenp->type = isc_tokentype_string; + if (escaped && c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + tokenp->type = (state == lexstate_string) + ? isc_tokentype_string + : isc_tokentype_vpair; tokenp->value.as_textregion.base = lex->data; tokenp->value.as_textregion.length = (unsigned int)(lex->max_token - @@ -753,6 +785,7 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { } break; case lexstate_qstring: + case lexstate_qvpair: if (c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; @@ -766,7 +799,10 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { INSIST(prev != NULL); *prev = '"'; } else { - tokenp->type = isc_tokentype_qstring; + tokenp->type = + (state == lexstate_qstring) + ? isc_tokentype_qstring + : isc_tokentype_qvpair; tokenp->value.as_textregion.base = lex->data; tokenp->value.as_textregion.length = @@ -876,7 +912,12 @@ isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token, ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE; isc_result_t result; - if (expect == isc_tokentype_qstring) { + if (expect == isc_tokentype_vpair) { + options |= ISC_LEXOPT_VPAIR; + } else if (expect == isc_tokentype_qvpair) { + options |= ISC_LEXOPT_VPAIR; + options |= ISC_LEXOPT_QVPAIR; + } else if (expect == isc_tokentype_qstring) { options |= ISC_LEXOPT_QSTRING; } else if (expect == isc_tokentype_number) { options |= ISC_LEXOPT_NUMBER; @@ -895,7 +936,12 @@ isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token, return (ISC_R_SUCCESS); } if (token->type == isc_tokentype_string && - expect == isc_tokentype_qstring) { + (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair)) + { + return (ISC_R_SUCCESS); + } + if (token->type == isc_tokentype_vpair && + expect == isc_tokentype_qvpair) { return (ISC_R_SUCCESS); } if (token->type != expect) { diff --git a/lib/isc/tests/lex_test.c b/lib/isc/tests/lex_test.c index 059d323bf0..f712d7e276 100644 --- a/lib/isc/tests/lex_test.c +++ b/lib/isc/tests/lex_test.c @@ -29,6 +29,10 @@ #include "isctest.h" +#define AS_STR(x) (x).value.as_textregion.base + +static bool debug = false; + static int _setup(void **state) { isc_result_t result; @@ -119,13 +123,297 @@ lex_setline(void **state) { isc_lex_destroy(&lex); } +static struct { + const char *text; + const char *string_value; + isc_result_t string_result; + isc_tokentype_t string_type; + const char *qstring_value; + isc_result_t qstring_result; + isc_tokentype_t qstring_type; + const char *qvpair_value; + isc_result_t qvpair_result; + isc_tokentype_t qvpair_type; +} parse_tests[] = { + { "", "", ISC_R_SUCCESS, isc_tokentype_eof, "", ISC_R_SUCCESS, + isc_tokentype_eof, "", ISC_R_SUCCESS, isc_tokentype_eof }, + { "1234", "1234", ISC_R_SUCCESS, isc_tokentype_string, "1234", + ISC_R_SUCCESS, isc_tokentype_string, "1234", ISC_R_SUCCESS, + isc_tokentype_string }, + { "1234=", "1234=", ISC_R_SUCCESS, isc_tokentype_string, + "1234=", ISC_R_SUCCESS, isc_tokentype_string, "1234=", ISC_R_SUCCESS, + isc_tokentype_vpair }, + { "1234=foo", "1234=foo", ISC_R_SUCCESS, isc_tokentype_string, + "1234=foo", ISC_R_SUCCESS, isc_tokentype_string, "1234=foo", + ISC_R_SUCCESS, isc_tokentype_vpair }, + { "1234=\"foo", "1234=\"foo", ISC_R_SUCCESS, isc_tokentype_string, + "1234=\"foo", ISC_R_SUCCESS, isc_tokentype_string, NULL, + ISC_R_UNEXPECTEDEND, 0 }, + { "1234=\"foo\"", "1234=\"foo\"", ISC_R_SUCCESS, isc_tokentype_string, + "1234=\"foo\"", ISC_R_SUCCESS, isc_tokentype_string, "1234=foo", + ISC_R_SUCCESS, isc_tokentype_qvpair }, + { "key", "key", ISC_R_SUCCESS, isc_tokentype_string, "key", + ISC_R_SUCCESS, isc_tokentype_string, "key", ISC_R_SUCCESS, + isc_tokentype_string }, + { "\"key=", "\"key=", ISC_R_SUCCESS, isc_tokentype_string, NULL, + ISC_R_UNEXPECTEDEND, 0, "\"key=", ISC_R_SUCCESS, + isc_tokentype_vpair }, + { "\"key=\"", "\"key=\"", ISC_R_SUCCESS, isc_tokentype_string, "key=", + ISC_R_SUCCESS, isc_tokentype_qstring, NULL, ISC_R_UNEXPECTEDEND, 0 }, + { "key=\"\"", "key=\"\"", ISC_R_SUCCESS, isc_tokentype_string, + "key=\"\"", ISC_R_SUCCESS, isc_tokentype_string, + "key=", ISC_R_SUCCESS, isc_tokentype_qvpair }, + { "key=\"a b\"", "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, + "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=a b", + ISC_R_SUCCESS, isc_tokentype_qvpair }, + { "key=\"a\tb\"", "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, + "key=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=a\tb", + ISC_R_SUCCESS, isc_tokentype_qvpair }, + /* double quote not immediately after '=' is not special. */ + { "key=c\"a b\"", "key=c\"a", ISC_R_SUCCESS, isc_tokentype_string, + "key=c\"a", ISC_R_SUCCESS, isc_tokentype_string, "key=c\"a", + ISC_R_SUCCESS, isc_tokentype_vpair }, + /* remove special meaning for '=' by escaping */ + { "key\\=", "key\\=", ISC_R_SUCCESS, isc_tokentype_string, + "key\\=", ISC_R_SUCCESS, isc_tokentype_string, + "key\\=", ISC_R_SUCCESS, isc_tokentype_string }, + { "key\\=\"a\"", "key\\=\"a\"", ISC_R_SUCCESS, isc_tokentype_string, + "key\\=\"a\"", ISC_R_SUCCESS, isc_tokentype_string, "key\\=\"a\"", + ISC_R_SUCCESS, isc_tokentype_string }, + { "key\\=\"a \"", "key\\=\"a", ISC_R_SUCCESS, isc_tokentype_string, + "key\\=\"a", ISC_R_SUCCESS, isc_tokentype_string, "key\\=\"a", + ISC_R_SUCCESS, isc_tokentype_string }, + /* vpair with a key of 'key\=' (would need to be deescaped) */ + { "key\\==", "key\\==", ISC_R_SUCCESS, isc_tokentype_string, + "key\\==", ISC_R_SUCCESS, isc_tokentype_string, + "key\\==", ISC_R_SUCCESS, isc_tokentype_vpair }, + { "key\\==\"\"", "key\\==\"\"", ISC_R_SUCCESS, isc_tokentype_string, + "key\\==\"\"", ISC_R_SUCCESS, isc_tokentype_string, + "key\\==", ISC_R_SUCCESS, isc_tokentype_qvpair }, + { "key=\\\\\\\\", "key=\\\\\\\\", ISC_R_SUCCESS, isc_tokentype_string, + "key=\\\\\\\\", ISC_R_SUCCESS, isc_tokentype_string, "key=\\\\\\\\", + ISC_R_SUCCESS, isc_tokentype_vpair }, + { "key=\\\\\\\"", "key=\\\\\\\"", ISC_R_SUCCESS, isc_tokentype_string, + "key=\\\\\\\"", ISC_R_SUCCESS, isc_tokentype_string, "key=\\\\\\\"", + ISC_R_SUCCESS, isc_tokentype_vpair }, + /* incomplete escape sequence */ + { "key=\\\"\\", NULL, ISC_R_UNEXPECTEDEND, isc_tokentype_string, NULL, + ISC_R_UNEXPECTEDEND, 0, NULL, ISC_R_UNEXPECTEDEND, 0 }, + /* incomplete escape sequence */ + { "key=\\", NULL, ISC_R_UNEXPECTEDEND, isc_tokentype_string, NULL, + ISC_R_UNEXPECTEDEND, 0, NULL, ISC_R_UNEXPECTEDEND, 0 }, +}; + +/*% + * string + */ +static void +lex_string(void **state) { + isc_buffer_t buf; + isc_lex_t *lex = NULL; + isc_result_t result; + isc_token_t token; + size_t i; + + UNUSED(state); + + for (i = 0; i < ARRAY_SIZE(parse_tests); i++) { + result = isc_lex_create(test_mctx, 1024, &lex); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_buffer_constinit(&buf, parse_tests[i].text, + strlen(parse_tests[i].text)); + isc_buffer_add(&buf, strlen(parse_tests[i].text)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_setsourceline(lex, 100); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(&token, 0, sizeof(token)); + result = isc_lex_getmastertoken(lex, &token, + isc_tokentype_string, true); + if (debug) { + fprintf(stdout, "# '%s' -> result=%s/%s, type=%u/%u\n", + parse_tests[i].text, isc_result_toid(result), + isc_result_toid(parse_tests[i].string_result), + token.type, parse_tests[i].string_type); + } + + assert_int_equal(result, parse_tests[i].string_result); + if (result == ISC_R_SUCCESS) { + switch (token.type) { + case isc_tokentype_string: + case isc_tokentype_qstring: + case isc_tokentype_vpair: + case isc_tokentype_qvpair: + if (debug) { + fprintf(stdout, "# value='%s'\n", + AS_STR(token)); + } + assert_int_equal(token.type, + parse_tests[i].string_type); + assert_string_equal( + AS_STR(token), + parse_tests[i].string_value); + break; + default: + assert_int_equal(token.type, + parse_tests[i].string_type); + break; + } + } + + isc_lex_destroy(&lex); + } +} + +/*% + * qstring + */ +static void +lex_qstring(void **state) { + isc_buffer_t buf; + isc_lex_t *lex = NULL; + isc_result_t result; + isc_token_t token; + size_t i; + + UNUSED(state); + + for (i = 0; i < ARRAY_SIZE(parse_tests); i++) { + result = isc_lex_create(test_mctx, 1024, &lex); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_buffer_constinit(&buf, parse_tests[i].text, + strlen(parse_tests[i].text)); + isc_buffer_add(&buf, strlen(parse_tests[i].text)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_setsourceline(lex, 100); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(&token, 0, sizeof(token)); + result = isc_lex_getmastertoken(lex, &token, + isc_tokentype_qstring, true); + if (debug) { + fprintf(stdout, "# '%s' -> result=%s/%s, type=%u/%u\n", + parse_tests[i].text, isc_result_toid(result), + isc_result_toid(parse_tests[i].qstring_result), + token.type, parse_tests[i].qstring_type); + } + + assert_int_equal(result, parse_tests[i].qstring_result); + if (result == ISC_R_SUCCESS) { + switch (token.type) { + case isc_tokentype_string: + case isc_tokentype_qstring: + case isc_tokentype_vpair: + case isc_tokentype_qvpair: + if (debug) { + fprintf(stdout, "# value='%s'\n", + AS_STR(token)); + } + assert_int_equal(token.type, + parse_tests[i].qstring_type); + assert_string_equal( + AS_STR(token), + parse_tests[i].qstring_value); + break; + default: + assert_int_equal(token.type, + parse_tests[i].qstring_type); + break; + } + } + + isc_lex_destroy(&lex); + } +} + +/*% + * keypair is =. This has implications double quotes + * in key names. + */ +static void +lex_keypair(void **state) { + isc_buffer_t buf; + isc_lex_t *lex = NULL; + isc_result_t result; + isc_token_t token; + size_t i; + + UNUSED(state); + + for (i = 0; i < ARRAY_SIZE(parse_tests); i++) { + result = isc_lex_create(test_mctx, 1024, &lex); + assert_int_equal(result, ISC_R_SUCCESS); + + isc_buffer_constinit(&buf, parse_tests[i].text, + strlen(parse_tests[i].text)); + isc_buffer_add(&buf, strlen(parse_tests[i].text)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_setsourceline(lex, 100); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(&token, 0, sizeof(token)); + result = isc_lex_getmastertoken(lex, &token, + isc_tokentype_qvpair, true); + if (debug) { + fprintf(stdout, "# '%s' -> result=%s/%s, type=%u/%u\n", + parse_tests[i].text, isc_result_toid(result), + isc_result_toid(parse_tests[i].qvpair_result), + token.type, parse_tests[i].qvpair_type); + } + + assert_int_equal(result, parse_tests[i].qvpair_result); + if (result == ISC_R_SUCCESS) { + switch (token.type) { + case isc_tokentype_string: + case isc_tokentype_qstring: + case isc_tokentype_vpair: + case isc_tokentype_qvpair: + if (debug) { + fprintf(stdout, "# value='%s'\n", + AS_STR(token)); + } + assert_int_equal(token.type, + parse_tests[i].qvpair_type); + assert_string_equal( + AS_STR(token), + parse_tests[i].qvpair_value); + break; + default: + assert_int_equal(token.type, + parse_tests[i].qvpair_type); + break; + } + } + + isc_lex_destroy(&lex); + } +} + int -main(void) { +main(int argc, char *argv[]) { const struct CMUnitTest tests[] = { - cmocka_unit_test(lex_0xff), - cmocka_unit_test(lex_setline), + cmocka_unit_test(lex_0xff), cmocka_unit_test(lex_keypair), + cmocka_unit_test(lex_setline), cmocka_unit_test(lex_string), + cmocka_unit_test(lex_qstring), }; + UNUSED(argv); + + if (argc > 1) { + debug = true; + } + return (cmocka_run_group_tests(tests, _setup, _teardown)); } diff --git a/lib/ns/client.c b/lib/ns/client.c index 097733474f..9ba1c89992 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -203,6 +203,7 @@ ns_client_endrequest(ns_client_t *client) { client->udpsize = 512; client->extflags = 0; client->ednsversion = -1; + client->additionaldepth = 0; dns_ecs_init(&client->ecs); dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE); diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h index 974e80dc1b..a99835bd4e 100644 --- a/lib/ns/include/ns/client.h +++ b/lib/ns/include/ns/client.h @@ -195,6 +195,7 @@ struct ns_client { uint16_t udpsize; uint16_t extflags; int16_t ednsversion; /* -1 noedns */ + uint16_t additionaldepth; void (*cleanup)(ns_client_t *); void (*shutdown)(void *arg, isc_result_t result); void * shutdown_arg; diff --git a/lib/ns/query.c b/lib/ns/query.c index a9b1cd9dbb..f369c48134 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -1649,7 +1649,8 @@ query_additionalauth(query_ctx_t *qctx, const dns_name_t *name, } static isc_result_t -query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { +query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype, + dns_rdataset_t *found) { query_ctx_t *qctx = arg; ns_client_t *client = qctx->client; isc_result_t result, eresult = ISC_R_SUCCESS; @@ -1833,6 +1834,13 @@ found: */ ns_client_keepname(client, fname, dbuf); + /* + * Does the caller want the found rdataset? + */ + if (found != NULL && dns_rdataset_isassociated(rdataset)) { + dns_rdataset_clone(rdataset, found); + } + /* * If we have an rdataset, add it to the additional data * section. @@ -2038,29 +2046,25 @@ addname: dns_message_addname(client->message, fname, DNS_SECTION_ADDITIONAL); } - fname = NULL; /* - * In a few cases, we want to add additional data for additional - * data. It's simpler to just deal with special cases here than - * to try to create a general purpose mechanism and allow the - * rdata implementations to do it themselves. - * - * This involves recursion, but the depth is limited. The - * most complex case is adding a SRV rdataset, which involves - * recursing to add address records, which in turn can cause - * recursion to add KEYs. + * In some cases, a record that has been added as additional + * data may *also* trigger the addition of additional data. + * This cannot go more than MAX_RESTARTS levels deep. */ - if (type == dns_rdatatype_srv && trdataset != NULL) { - /* - * If we're adding SRV records to the additional data - * section, it's helpful if we add the SRV additional data - * as well. - */ - eresult = dns_rdataset_additionaldata( - trdataset, query_additional_cb, qctx); + if (trdataset != NULL && dns_rdatatype_followadditional(type)) { + if (client->additionaldepth++ < MAX_RESTARTS) { + eresult = dns_rdataset_additionaldata( + trdataset, fname, query_additional_cb, qctx); + } + client->additionaldepth--; } + /* + * Don't release fname. + */ + fname = NULL; + cleanup: CTRACE(ISC_LOG_DEBUG(3), "query_additional_cb: cleanup"); ns_client_putrdataset(client, &rdataset); @@ -2112,7 +2116,8 @@ query_setorder(query_ctx_t *qctx, dns_name_t *name, dns_rdataset_t *rdataset) { * Handle glue and fetch any other needed additional data for 'rdataset'. */ static void -query_additional(query_ctx_t *qctx, dns_rdataset_t *rdataset) { +query_additional(query_ctx_t *qctx, dns_name_t *name, + dns_rdataset_t *rdataset) { ns_client_t *client = qctx->client; isc_result_t result; @@ -2149,7 +2154,8 @@ regular: * Add other additional data if needed. * We don't care if dns_rdataset_additionaldata() fails. */ - (void)dns_rdataset_additionaldata(rdataset, query_additional_cb, qctx); + (void)dns_rdataset_additionaldata(rdataset, name, query_additional_cb, + qctx); CTRACE(ISC_LOG_DEBUG(3), "query_additional: done"); } @@ -2230,7 +2236,7 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep, */ query_addtoname(mname, rdataset); query_setorder(qctx, mname, rdataset); - query_additional(qctx, rdataset); + query_additional(qctx, mname, rdataset); /* * Note: we only add SIGs if we've added the type they cover, so diff --git a/util/copyrights b/util/copyrights index 931e52ee9f..0f4dab432d 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1497,6 +1497,8 @@ ./lib/dns/rdata/in_1/dhcid_49.h C 2006,2007,2016,2018,2019,2020,2021 ./lib/dns/rdata/in_1/eid_31.c C 2018,2019,2020,2021 ./lib/dns/rdata/in_1/eid_31.h C 2018,2019,2020,2021 +./lib/dns/rdata/in_1/https_65.c C 2019,2020,2021 +./lib/dns/rdata/in_1/https_65.h C 2019,2020,2021 ./lib/dns/rdata/in_1/kx_36.c C 1999,2000,2001,2003,2004,2005,2007,2009,2015,2016,2017,2018,2019,2020,2021 ./lib/dns/rdata/in_1/kx_36.h C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/dns/rdata/in_1/nimloc_32.c C 2018,2019,2020,2021 @@ -1509,6 +1511,8 @@ ./lib/dns/rdata/in_1/px_26.h C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 ./lib/dns/rdata/in_1/srv_33.c C 1999,2000,2001,2003,2004,2005,2007,2009,2015,2016,2018,2019,2020,2021 ./lib/dns/rdata/in_1/srv_33.h C 1999,2000,2001,2004,2005,2007,2016,2018,2019,2020,2021 +./lib/dns/rdata/in_1/svcb_64.c C 2019,2020,2021 +./lib/dns/rdata/in_1/svcb_64.h C 2019,2020,2021 ./lib/dns/rdata/in_1/wks_11.c C 1999,2000,2001,2002,2004,2007,2009,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 ./lib/dns/rdata/in_1/wks_11.h C 1999,2000,2001,2004,2007,2016,2018,2019,2020,2021 ./lib/dns/rdata/rdatastructpre.h C 1999,2000,2001,2004,2007,2016,2018,2019,2020,2021