From e8c250bb83d125815e71fffa1a37aebfe122217c Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Mon, 30 Mar 2026 11:10:40 -0400 Subject: [PATCH] import CS unbound-1.24.2-2.el9 --- .gitignore | 2 +- .unbound.metadata | 2 +- SOURCES/Yorgos.asc | 128 + SOURCES/root.anchor | 1 + SOURCES/root.key | 2 +- SOURCES/tmpfiles-unbound-libs.conf | 2 + SOURCES/unbound-1.16-CVE-2022-3204.patch | 218 -- ...d-1.16-CVE-2023-50387-CVE-2023-50868.patch | 2304 ----------------- SOURCES/unbound-1.16-control-key-perms.patch | 14 - SOURCES/unbound-1.16-control-t-flag.patch | 129 - SOURCES/unbound-1.16.2.tar.gz.asc | 16 - SOURCES/unbound-1.21-CVE-2024-8508.patch | 249 -- SOURCES/unbound-1.23.1-CVE-2025-5994.patch | 2255 ---------------- SOURCES/unbound-1.24.2.tar.gz.asc | 17 + ...bound-1.25-tls-crypto-policy-default.patch | 61 + SOURCES/unbound-1.25-tls-crypto-policy.patch | 483 ++++ SOURCES/unbound-fedora-config.patch | 113 + SPECS/unbound.spec | 81 +- 18 files changed, 854 insertions(+), 5223 deletions(-) create mode 100644 SOURCES/Yorgos.asc create mode 100644 SOURCES/tmpfiles-unbound-libs.conf delete mode 100644 SOURCES/unbound-1.16-CVE-2022-3204.patch delete mode 100644 SOURCES/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch delete mode 100644 SOURCES/unbound-1.16-control-key-perms.patch delete mode 100644 SOURCES/unbound-1.16-control-t-flag.patch delete mode 100644 SOURCES/unbound-1.16.2.tar.gz.asc delete mode 100644 SOURCES/unbound-1.21-CVE-2024-8508.patch delete mode 100644 SOURCES/unbound-1.23.1-CVE-2025-5994.patch create mode 100644 SOURCES/unbound-1.24.2.tar.gz.asc create mode 100644 SOURCES/unbound-1.25-tls-crypto-policy-default.patch create mode 100644 SOURCES/unbound-1.25-tls-crypto-policy.patch create mode 100644 SOURCES/unbound-fedora-config.patch diff --git a/.gitignore b/.gitignore index 5c781e9..ff9ad76 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/unbound-1.16.2.tar.gz +SOURCES/unbound-1.24.2.tar.gz diff --git a/.unbound.metadata b/.unbound.metadata index c8b7a90..a90817b 100644 --- a/.unbound.metadata +++ b/.unbound.metadata @@ -1 +1 @@ -9aea0e923b9d6779b5bc360094e24a4017e2bb25 SOURCES/unbound-1.16.2.tar.gz +89220193962044660fb5ec375601b3faccd4bd5b SOURCES/unbound-1.24.2.tar.gz diff --git a/SOURCES/Yorgos.asc b/SOURCES/Yorgos.asc new file mode 100644 index 0000000..e18ec55 --- /dev/null +++ b/SOURCES/Yorgos.asc @@ -0,0 +1,128 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFfYHeYBEAC/8SdeXNspt9ZIoZRSL9juNLHA17TXcHdKSthgWBtwwWZbUPq8 +SJr7Y+hr6jMCDKY9800QzLF0nLkyXnZgaBcvR0rRbCT/qvALJ0fpfjcotapZ1hBv +omb9s8Bo28uKn8tbTMXYNsElUae4Ch/CrU1vfe50YoyQgLR8UBa15gV+2RmC+6jI +qxDYS8sylWlDn6Qim+77feLlObPnNdzgfWGZo14eJByTsz0qrh8aS/BS1FAsnEQ6 +W6AqukhpuKuWvoAUXKjfguXQolxeexubmKaLcGOTvecw+cbh/a5SPHRtRVr9qTxp +elk6UEpakY5K9UtZkrG55VWih/4KqY9bNyhJBtpAk1fXA+mYfx5BcFpECYdU9kz4 +UgV5jK0HYRHQTLC91PPVQgH86we+Aae6TaJneCLEIzBK36TgAP8RKrvFfPUym5OP +YbWOom27QTKfRVcyxPKglJxrTSWixnKWS/pqxNY8hF9Ne4crRAF4wX2yBVbGnjNr +S9TpYmjMwURbuYm+rWZk/8w5OJG60V3wax56c0jn/42O3Y2hzQ+PbOv2M4UuuajS +2YL3/KUsRLBapUpPQjzChwzdr/vzFEhk9XxK2VGMN+dh2HjYwDFendc5csyt/cVr +g3LssVS2bKy5g3IhrzCKAk0Sky4S5t/mcN+lWztNvCijuLz58GCym5GwJQARAQAB +tCtZb3Jnb3MgVGhlc3NhbG9uaWtlZnMgPHlvcmdvc0BubG5ldGxhYnMubmw+iQJX +BBMBCABBAhsjBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAhkBFiEElI60IyLF0At5 +NA9dz/M0TZCHpJAFAmbz0CwFCRD85cYACgkQz/M0TZCHpJBVnhAAkcd79Twxj/tt +C4q2Xpq75+Ew6YR9gLqYiV5vEd6fu0oyhuVoUlfTkjH4ALIoGIKaO9yAVUXsrGrs +n1aJPo1Mw3q6mIwtQOxXz/W44LuFzcvZkHtCYX4YyLrUHXZPvl+r4eYkTOcyyQMU +BmbuvWhufv8MBilvWltQLxfLlgihbfuIrxqjAYDhqCffpgUiZyCut2rrenIgeh1f +DvC+GjZ3cfb1UsIpzm19yr4NCiTHkLkCOAAcAUFwWWeO/jfUsSvFQEHUnYNRREzI +Lth9NlKwPtsOVi+wcNnWQtFQb11BMr407xBib7hLSIFiqvOiYgQABjZdWN+snCRP +ZZSruigjE9ateOloJwmBqrSJLAywxvDE0ivqfSj51W5eJc/JLSXvjrOuW28dJCr8 +RV9PjC9X7zuTiFLzV8SVH71Z43Rix8n7AOp3wgRe3SygEyQXPj4qbm5HHVsx9GEA +zn495L99dJ4wZgjkbEsGhzwUx7N9FHEGS/sz3LiEq+ZYckR6gzTMMrQJgbsS9lnK +9xlXsp37uIYvx9W9JZXtS+AZhw0q3osMYBF68HPX8B9GBYlkQWmWSIMfzRYcD2n1 +5+XsfERK4mxcTWl2sCYpt8Sy3tADj9nQDabAlGUd/hlFS1dvDVQjGh6ER5S0nZjY +nmRsLl8nOTKhb2xY+2p1sDjxxQYJJQe0K0dlb3JnZSBUaGVzc2Fsb25pa2VmcyA8 +Z2VvcmdlQG5sbmV0bGFicy5ubD6JAlQEEwEIAD4CGyMFCwkIBwIGFQgJCgsCBBYC +AwECHgECF4AWIQSUjrQjIsXQC3k0D13P8zRNkIekkAUCZvPQPQUJEPzlxgAKCRDP +8zRNkIekkFfCEACheY1yr2Z+LPjm/Nd2eA4CFFO7nUQHI+a6lYBd57txrRuIicuG +pGjOhnvcioRwICiKNLJD3YTU+WOd+sbO7BXH2sw2KdU9NK1ojKX/SQiTg6upfJsu +gbgar2oPvR88B7oSiuonZnhEf72HfWKDSBXHpi6KC6S3JZ+o50NB3GBpwUL1lfKW +ovymYbN6tYQfqw/+AP5jUUNpkclC0RbcW69rpvrHHqeQV1AVKkm/jNQpWLKYTGF7 +bbdLkgMh3rHp8gmF0/GuK+oyL7xD+TEXfr3iqlDIVuxbxDN8xTti1RrERU/MWQar +qOSFZcr4t+nlwThJidDLF/u3h0Ymrjz92VTfCgELIwCKxGX7jAyLZHzuWAp+0Pr/ +yuHodbweGNcGVoXmIpK93/WZcfFlBcyQLECVcijmxd7Euk0xDk76RQpuuL5VOWqn +aZcf2uNfppwKFZJjcXwK+EQbwN7+RFNvLrwoRn/1xM57T5AYBAgSvKb6h0G5KwW6 +tJfJdSu1MHfCZS1hH1Gr4+UG+VbLCVmQ9N/lUs0bcD7pK+bA1W0YnsIVuaQ+YZUh +KrJoCUF0kVDtW9ETZkp0iVBm1Q9xgTGaxUTVmctOyAbdCLyHNra4fo1BAdGlu+IP +qAcktaBUKFxWxRxf9O5kGihce9anK8CJ/TCnQ7wSvyYrlAoBoQaS78VzYYkBHAQS +AQIABgUCV9kUOAAKCRAwkY2CdXJCIju1B/oCvf1kWYndNeLS6U2O6DtFAL2Ia5tY +Zukcyqb1hkYcrBiMZbQN94gX5a+6Q4pdd2n241r1ZuSWdwUhRUbF4mvbZMVsavnk +cvrRGviVIUXf71W0O/IsmQ9oN0Finhpf14Y4z/xqF8DpvJdkWc6X5g+RJuko6q2w +B7x2UfSyF4USp47LSmUQnC59IF8jzaElgBha3gwEuXL3d2qBepoV/e9RiXJClUAT ++O7qdxzDq1eiZ+NXUjDCmrkuWjHLAZAv0jx1KUTCQ2no62UM95igtJ+Kn56Lc2+J +CqFJztaZeX8CgXXryxNpsyhZJ33dIoLCT03K25wrV5Y+eag05slQ9sC3iQI9BBMB +CAAnBQJX2RCwAhsjBQkFo5qABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEM/z +NE2Qh6SQ3JUP/2G3bRNObS7zsfN3rjkbjLxaDOwNggRdbeXM5rHDVEG9SWesCGaI +vyQdkSGQoIaKUgNv7Yp8O8pEnD4IwdhNSaXVIB3pBtdOD0UM1wuxRpfqJOUxZEoW +T2Jr31Tg2qepp6nT7UmdiF7uCBDy8Jm6k6Q+6UT58cPaesRQdSPi6Go7ho2/xVvK +Ve9ufSTSTdG5+7bJDu6Iv7sydKUEG4jPDqo+jjVLn1X6Rfp+E4JAvOvFrSJHW5sa +A332xV40GeV+aM1ndP7dPkz8+AGB3QD7JF2DLcqvLo0TYOvjnlOGYcNp8gzp23g9 +KFwe2sdbdtVpuWaJUSpXXiUZnFzrrVxDNiEBjqsPa5ysOxzJ+1gUbcrIjUeNeAFh +us4XL+IidPATnhTIX3X/uPRB87KaTaA8XUqsuSd2DM9mLxdHKC9Jf8D1t+ywYrek +Cp+K80vCtFPWBM4+w8nGugTNKJEGIXZDGFOF/c7r6xKkaOYK0Y+IGJawlV5LaADl +BmQpPk0ubYclwb07FcegaHSxxIqUo/kbyt1YV5mU+QVymZ+xyvIBrnW8hBuNWRvU +5acnIZibCERayo8ZuI+r/X3bLHfDx0oh2h+cL3utNZUqmgZNR0Di8P+x0hUYsYPO +TJaDBSgvxUtY0Ci+OWX38kffGGvhW3CM8V6skdVc8cp7Db7gxase4BxxtC5HZW9y +Z2UgVGhlc3NhbG9uaWtlZnMgPGdlb3JnZUBvcGVubmV0bGFicy5jb20+iQJUBBMB +CAA+AhsjBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAFiEElI60IyLF0At5NA9dz/M0 +TZCHpJAFAmbz0D0FCRD85cYACgkQz/M0TZCHpJBnFw/8CRLGJnNAP43mBniIP5R1 +/10i4xG5s1Ka/y5C3aRgZUNaGMPLF8VmrC26HTPNhmduhn3j9gnBuSHgRAJUWs2K +o1q0A2/O5fFJvqPyEUl30gG8qkzFl5UGRUr7VNtBa6VpI7g78d3P4/H8THB0tYZ3 +GZv980QXwTE11aXjvPQu4e8sMOR1OVEEH+6hW1T0SvEKAMV1BHwuZAmC6HTfx5e7 +iGNWu/dwJsmwzqcAkuTTSqlmzZdIjZWJDL8pfnschkVilC3pEpEk5ExSkt/onOD2 +WCAKJUiPR6gRI2H6fE0PF8iG9isisvNhQ3MrWUIKS+1WOotoG7Bu7ob46viJKQuN +9t7KBqjdftjJHjmVop3mfX0UUEDPjkZXK5R/aUspXi4IGdM+9JijqxveicQegOhM +LcE8039Z0AaXn9IA0kQB05A4a+CEnoPL7qe+fIBJM5hZDrpMe4fAAGxiQzbRpdkZ +CrXmT+CRkhc/BvUJ5yoE1q++9Fw9eyMbPOak6GLckCIPy+Mqi4z+ZXNCtcPs3Qbc +/7AY8qyswRsD2t5bbe4g+fLEt9IsN3UvKFUKnQ88jcn9Zmps69msMDm9jEj/qo+j +QCriLLu8E1ZwhedNVOQN89w4Zww/BUyEnL4hng8Tw+RTV8Jtq5EvAleW5sZsnTzA +zn1ysZUyO/Pu7Br2jnGRAQyJARwEEgECAAYFAlfZFDwACgkQMJGNgnVyQiIHjAf/ +VrPMgIRjRTYi4cxOr5ewaim1hgJZO1oCJoMopwIvpZ2kAUqL/uPMT3wREe5bi79H +jXYcaX+RbrV4ZdzaajDUFCj867KGErxqtRkANJ1eNLcQmVwGNoFeTQbgEOBfKq1t +hRfMqHF3fxCPJp4z4U3kBPUpIQPERjgUdkH8fxZ34Omo1SLO1b0dVqsneezccBVv +Hj7gVoaWw65Ov7vngwNCKH3fjOkcoINTH/nEw4WWh6UV5ZN49GRqa6oWdUJMZbgB +w8z357RLN6YLe7KMh4oGpUIvfH6Vfq6CIb6s9pfgjAvq8O7p9n0/0FG77j84MuGw +fdhq8eSazO/j9LUCBM+8k4kCPQQTAQgAJwUCV9kQiwIbIwUJBaOagAULCQgHAgYV +CAkKCwIEFgIDAQIeAQIXgAAKCRDP8zRNkIekkN9pD/wI8JAyjJEIjUJNLRuARDDv +pJrQjAo/02naPcGs4yyUd7yRkhzVKLFLvif8XICxxLWk6FdT0PJeKGTvRoe2+Rje +c14rO30niRymWkBi36iDW46Dpt7Jx+LUDhUMYPL+woKSoHmbBGWLSYXKxaD8F93A +nVs97nP4PWpspv2BFiuwKGsSsOyyQPPvr7jCin3H5oPH9qDnIV0KonAYbzzEKod5 +t0Rgzo/nWXZBFXWC5xvKeghwkdT++gYS/ThvQY2ua6A1XRE8BntyldD081NPi3NO +dWa9m8ufFOJsEEiWcpdT+EWoDw5JyGAR7U3IOVl3BTo7shdcYEvRVrDMBpac+ItG +WvogUv7alBdHWi48amvZE06RI/nDJ/rxj13S/4POgMHU++aQI5a1G5H3jBu4cehH +4iT1UKmozfzVEfcHb2dsaKnnuFzQxmol0lZu1ETyof+Lxvs+wErN0QR+VDNweJEJ +PMXiEcjASdLtrEKgFSP2B5yGGzt93C+HbD+VQOU359aAnvVjbTAVz8izuMphd6Bz +Ix1q//q2VmxqjjT3Iv30hBRX02x2M8gsP/e49XWEll7stkMtbYhBU0sHQ2CqzLGh +gJN3ecpi2sKWVqN8HUZOwJFj6f9ZX76YSM23wIugHfscMAVJUXvBrbd151WIshOf +FFPo62sYGt+SEMXWeRcHjbQuWW9yZ29zIFRoZXNzYWxvbmlrZWZzIDx5b3Jnb3NA +b3Blbm5ldGxhYnMuY29tPokCVAQTAQgAPgIbIwULCQgHAgYVCAkKCwIEFgIDAQIe +AQIXgBYhBJSOtCMixdALeTQPXc/zNE2Qh6SQBQJm89A9BQkQ/OXGAAoJEM/zNE2Q +h6SQOf4QAIp5fEn+vVOqDuLrv8Li61UDVPE3v5b9ocPR7OMENeFpRH7K2p8xFkAM +f6JeS2ehbIjyUS8iGc+mZOalvZynJdOiHtys5r4lZanm1Rl+mWXb+nHGE/Oi4gQ3 +aEF/AwolbKi9oNXAiCtA3hmaI6FYWtAh5XOnyMG140dhlXMWzvN1ZAWXWioS33Fp +n9Z7xOsyG2Bmky69JjUQPD119noD4pEFtCipciiACVNdHGfI8QDT/8pMAxv/Q7tW ++7gyFztT1XvKONWyCfHjf1x60JQNPPM31x3xUVRz/sK+GyLq6VLiydvSZeUX3CzM +4bHixKKkSyvsX+bc9K/iLrXwZFhRdrRbpVQFpsdxv48mN5WsDlpN17KgMCyNiMDV +0VagYjZC5AurOo2mmS7PKAYGILaq3YwQ3nXYiuWfdXVW6EXtw/vdFOjFU6ppbaA3 +1+xyMDOLSGLr7f+gdZvlEc+5MX1F3mHpmuKN7clUju/HEOdVq1Bbla7BplPgqCaH +ZYCg/VHNt7ue7MYeEgTJ8OGgUDRNPa3PdU3YHRCuwJH1UzinD39K8R9/g9qiGJbC +87//1FGZp9lhIE5tja6F5pJ5GwJK9zC0iGj2NrwBvTKhuyF6W3ZQc2voYQn+gIq4 +sfbned7qpvpjLcMgIv/y82iVm2EtKtKYl982aA9S53pHqjV27kieuQINBFfYHeYB +EAC2h9yjSe2SgtcB0H+E0ndaewaZaQCE7q+RO43dotGH9eFnVwE4/ftcK1SN42ih +lF5OnTaKPyXvgQ6U8W8VB8eLjeTwA/dSXuJX7kJpEK8saPqJP6zTUmPqp/GSzS6Y +rhKLfpFn4chmywpDFcGNMz0sYXiJgPqKL7W0KuG+ziPToAeWl8ckeXyl77/lHVhW +YylaQJEASklqCViPXSp9vI7/57UEm4MQPXwsDBOwuVVqcSu3ZM5MtY9XlbVPNCYm +ZIMqmh8HgYwbiq9dTfJi+6v17+uDQGZewWK/WwFM+9dDx7YkTeOBiUduYtJPW64N +W/RJ7pskbLAy+OZApTZWg0cISN6GOmPN3F0AiWzUjvSMREHhFHyxj4Y15vuDOFvP +GFxr4xBiyMX1JLCKK6OFnyPfoJ9v/o3UgrQgLrfXCmKdvkwBCgJvN3Fsxzha6Dtf +6RcZ02fr7SCZZhdBrlrflvC1uWZ0g3A87ss7h4Iw3njlO3aX6Bo9R4VOLUkiRKi4 +hmQBxPvXxI2ERmKRomo6lrMaDMzIjD4APSM1vUfZguzQxVYpM8lwy1COeqxsj5p+ +LH6f/EU+4dXZwooJ1uanBOvG2ntnz8SErE+e7wNYE4a/fb8xYM4j7p6qYtnNZPb8 +sj8bvx8iWXp4A1csVetyVSchBhTVQhhNos6ouYpc4ibrYwARAQABiQI8BBgBCAAm +AhsMFiEElI60IyLF0At5NA9dz/M0TZCHpJAFAmbz0dUFCRD8528ACgkQz/M0TZCH +pJA7hg/7Bh0jb7nrp2EuU4BWK55VG+3zbrye/NdDy6eo3sVVOOO1r+jBMoJK3m1A +GWUx40ogZjRn8GMtJfxkL6wsep2P775smm7x1TH6s2dgreTj9J66gitKEgxF0tjo +JztmGJ8YKSGE4wKi37KvvSqCm1ecA8akBzJVo0B8/GtXpg+y/q3/KSY1ujW7Ihu3 +60JXT1FRXOiYfrzUKIBm8/UVnv7guPudaJf0eU4btoy4Ywzn1UXyi8BdxPIQrQDR +tt69ffcjX8BIEloK7FURLre/LhbVxlssdWYIEFQhIlb+nghZlUbWHf0Ue3L0SFFS +xV5otzQL2WjJKtCnTpSopSUmwYyT7wyAL1RokOemXL47WOfiLjiGuS+K/4lT7VHS +fdLsYinS0LYr5dmhc05s//kLyQ0OSKNh3SNAN+lM/klLE/pFwM4mo56C+seNEvCm +sT53VPOOwp6JwsLKSqm8pu1fbVjT6laMc9BPi6KUjN/f7ZahCXNXOrA2uLnTlxw/ +ns1sOXSWZDWciZeL3kJeUQER5YZ59hzLYWAiJ+5KblWRlBMXb71FUp0Mh9V3dA5O +BX3IlcF54qE50chGzLnfQf1YLuh13xxxc2WsdMZjiCj8CVDMkD6ekShfQK8nyQsK +SJgdXcnw1CxcAVvsROtecUiD+DWrJcYExjSZ+zcI4+aRhp7uNt8= +=iknu +-----END PGP PUBLIC KEY BLOCK----- diff --git a/SOURCES/root.anchor b/SOURCES/root.anchor index c78ee03..1559542 100644 --- a/SOURCES/root.anchor +++ b/SOURCES/root.anchor @@ -1 +1,2 @@ +. 172800 IN DNSKEY 257 3 8 AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;{id = 38696 (ksk), size = 2048b} . 172800 IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} diff --git a/SOURCES/root.key b/SOURCES/root.key index 6c5622c..94d2e23 100644 --- a/SOURCES/root.key +++ b/SOURCES/root.key @@ -1,6 +1,6 @@ ; // The root key in bind format. This can be read by most tools, including ; // named, unbound, et. For libunbound, use ub_ctx_trustedkeys() to load this trusted-keys { +"." 257 3 8 "AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNnOAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6irwCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKEpUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBuzZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc="; // key id = 38696 "." 257 3 8 "AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU="; // key id = 20326 - }; diff --git a/SOURCES/tmpfiles-unbound-libs.conf b/SOURCES/tmpfiles-unbound-libs.conf new file mode 100644 index 0000000..d71ea46 --- /dev/null +++ b/SOURCES/tmpfiles-unbound-libs.conf @@ -0,0 +1,2 @@ +d /var/lib/unbound 0755 unbound unbound - +L /var/lib/unbound/root.key - - - - ../../../etc/unbound/dnssec-root.key diff --git a/SOURCES/unbound-1.16-CVE-2022-3204.patch b/SOURCES/unbound-1.16-CVE-2022-3204.patch deleted file mode 100644 index 2d2da91..0000000 --- a/SOURCES/unbound-1.16-CVE-2022-3204.patch +++ /dev/null @@ -1,218 +0,0 @@ -From 7af485f0fc9926425681ba0280ab6c2c8dd04530 Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 21 Sep 2022 11:10:38 +0200 -Subject: [PATCH] - Patch for CVE-2022-3204 Non-Responsive Delegation Attack. - ---- - unbound-1.16.2/iterator/iter_delegpt.c | 3 +++ - unbound-1.16.2/iterator/iter_delegpt.h | 2 ++ - unbound-1.16.2/iterator/iter_utils.c | 3 +++ - unbound-1.16.2/iterator/iter_utils.h | 9 +++++++ - unbound-1.16.2/iterator/iterator.c | 36 +++++++++++++++++++++++++- - unbound-1.16.2/services/cache/dns.c | 3 +++ - unbound-1.16.2/services/mesh.c | 7 +++++ - unbound-1.16.2/services/mesh.h | 11 ++++++++ - 8 files changed, 73 insertions(+), 1 deletion(-) - -diff --git a/unbound-1.16.2/iterator/iter_delegpt.c b/unbound-1.16.2/iterator/iter_delegpt.c -index 4bffa1b..fd07aaa 100644 ---- a/unbound-1.16.2/iterator/iter_delegpt.c -+++ b/unbound-1.16.2/iterator/iter_delegpt.c -@@ -78,6 +78,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region) - if(!delegpt_add_ns(copy, region, ns->name, ns->lame, - ns->tls_auth_name, ns->port)) - return NULL; -+ copy->nslist->cache_lookup_count = ns->cache_lookup_count; - copy->nslist->resolved = ns->resolved; - copy->nslist->got4 = ns->got4; - copy->nslist->got6 = ns->got6; -@@ -121,6 +122,7 @@ delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name, - ns->namelen = len; - dp->nslist = ns; - ns->name = regional_alloc_init(region, name, ns->namelen); -+ ns->cache_lookup_count = 0; - ns->resolved = 0; - ns->got4 = 0; - ns->got6 = 0; -@@ -620,6 +622,7 @@ int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, uint8_t lame, - } - ns->next = dp->nslist; - dp->nslist = ns; -+ ns->cache_lookup_count = 0; - ns->resolved = 0; - ns->got4 = 0; - ns->got6 = 0; -diff --git a/unbound-1.16.2/iterator/iter_delegpt.h b/unbound-1.16.2/iterator/iter_delegpt.h -index 62c8edc..586597a 100644 ---- a/unbound-1.16.2/iterator/iter_delegpt.h -+++ b/unbound-1.16.2/iterator/iter_delegpt.h -@@ -101,6 +101,8 @@ struct delegpt_ns { - uint8_t* name; - /** length of name */ - size_t namelen; -+ /** number of cache lookups for the name */ -+ int cache_lookup_count; - /** - * If the name has been resolved. false if not queried for yet. - * true if the A, AAAA queries have been generated. -diff --git a/unbound-1.16.2/iterator/iter_utils.c b/unbound-1.16.2/iterator/iter_utils.c -index 3e13e59..56b184a 100644 ---- a/unbound-1.16.2/iterator/iter_utils.c -+++ b/unbound-1.16.2/iterator/iter_utils.c -@@ -1209,6 +1209,9 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env, - struct delegpt_ns* ns; - size_t num = delegpt_count_targets(dp); - for(ns = dp->nslist; ns; ns = ns->next) { -+ if(ns->cache_lookup_count > ITERATOR_NAME_CACHELOOKUP_MAX_PSIDE) -+ continue; -+ ns->cache_lookup_count++; - /* get cached parentside A */ - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qinfo->qclass, -diff --git a/unbound-1.16.2/iterator/iter_utils.h b/unbound-1.16.2/iterator/iter_utils.h -index 8583fde..850be96 100644 ---- a/unbound-1.16.2/iterator/iter_utils.h -+++ b/unbound-1.16.2/iterator/iter_utils.h -@@ -62,6 +62,15 @@ struct ub_packed_rrset_key; - struct module_stack; - struct outside_network; - -+/* max number of lookups in the cache for target nameserver names. -+ * This stops, for large delegations, N*N lookups in the cache. */ -+#define ITERATOR_NAME_CACHELOOKUP_MAX 3 -+/* max number of lookups in the cache for parentside glue for nameserver names -+ * This stops, for larger delegations, N*N lookups in the cache. -+ * It is a little larger than the nonpside max, so it allows a couple extra -+ * lookups of parent side glue. */ -+#define ITERATOR_NAME_CACHELOOKUP_MAX_PSIDE 5 -+ - /** - * Process config options and set iterator module state. - * Sets default values if no config is found. -diff --git a/unbound-1.16.2/iterator/iterator.c b/unbound-1.16.2/iterator/iterator.c -index 25e5cfe..da9b799 100644 ---- a/unbound-1.16.2/iterator/iterator.c -+++ b/unbound-1.16.2/iterator/iterator.c -@@ -1218,6 +1218,15 @@ generate_dnskey_prefetch(struct module_qstate* qstate, - (qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){ - return; - } -+ /* we do not generate this prefetch when the query list is full, -+ * the query is fetched, if needed, when the validator wants it. -+ * At that time the validator waits for it, after spawning it. -+ * This means there is one state that uses cpu and a socket, the -+ * spawned while this one waits, and not several at the same time, -+ * if we had created the lookup here. And this helps to keep -+ * the total load down, but the query still succeeds to resolve. */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ return; - - /* if the DNSKEY is in the cache this lookup will stop quickly */ - log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch", -@@ -1911,6 +1920,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, - return 0; - } - query_count++; -+ /* If the mesh query list is full, exit the loop here. -+ * This makes the routine spawn one query at a time, -+ * and this means there is no query state load -+ * increase, because the spawned state uses cpu and a -+ * socket while this state waits for that spawned -+ * state. Next time we can look up further targets */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ break; - } - /* Send the A request. */ - if(ie->supports_ipv4 && -@@ -1925,6 +1942,9 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, - return 0; - } - query_count++; -+ /* If the mesh query list is full, exit the loop. */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ break; - } - - /* mark this target as in progress. */ -@@ -2085,6 +2105,15 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, - } - ns->done_pside6 = 1; - query_count++; -+ if(mesh_jostle_exceeded(qstate->env->mesh)) { -+ /* Wait for the lookup; do not spawn multiple -+ * lookups at a time. */ -+ verbose(VERB_ALGO, "try parent-side glue lookup"); -+ iq->num_target_queries += query_count; -+ target_count_increase(iq, query_count); -+ qstate->ext_state[id] = module_wait_subquery; -+ return 0; -+ } - } - if(ie->supports_ipv4 && !ns->done_pside4) { - /* Send the A request. */ -@@ -2560,7 +2589,12 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, - if(iq->depth < ie->max_dependency_depth - && iq->num_target_queries == 0 - && (!iq->target_count || iq->target_count[TARGET_COUNT_NX]==0) -- && iq->sent_count < TARGET_FETCH_STOP) { -+ && iq->sent_count < TARGET_FETCH_STOP -+ /* if the mesh query list is full, then do not waste cpu -+ * and sockets to fetch promiscuous targets. They can be -+ * looked up when needed. */ -+ && !mesh_jostle_exceeded(qstate->env->mesh) -+ ) { - tf_policy = ie->target_fetch_policy[iq->depth]; - } - -diff --git a/unbound-1.16.2/services/cache/dns.c b/unbound-1.16.2/services/cache/dns.c -index 6bca8d8..b6e5697 100644 ---- a/unbound-1.16.2/services/cache/dns.c -+++ b/unbound-1.16.2/services/cache/dns.c -@@ -404,6 +404,9 @@ cache_fill_missing(struct module_env* env, uint16_t qclass, - struct ub_packed_rrset_key* akey; - time_t now = *env->now; - for(ns = dp->nslist; ns; ns = ns->next) { -+ if(ns->cache_lookup_count > ITERATOR_NAME_CACHELOOKUP_MAX) -+ continue; -+ ns->cache_lookup_count++; - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { -diff --git a/unbound-1.16.2/services/mesh.c b/unbound-1.16.2/services/mesh.c -index 30bcf7c..2a41194 100644 ---- a/unbound-1.16.2/services/mesh.c -+++ b/unbound-1.16.2/services/mesh.c -@@ -2240,3 +2240,10 @@ mesh_serve_expired_callback(void* arg) - mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c, &tv); - } - } -+ -+int mesh_jostle_exceeded(struct mesh_area* mesh) -+{ -+ if(mesh->all.count < mesh->max_reply_states) -+ return 0; -+ return 1; -+} -diff --git a/unbound-1.16.2/services/mesh.h b/unbound-1.16.2/services/mesh.h -index 3be9b63..25121a6 100644 ---- a/unbound-1.16.2/services/mesh.h -+++ b/unbound-1.16.2/services/mesh.h -@@ -685,4 +685,15 @@ struct dns_msg* - mesh_serve_expired_lookup(struct module_qstate* qstate, - struct query_info* lookup_qinfo); - -+/** -+ * See if the mesh has space for more queries. You can allocate queries -+ * anyway, but this checks for the allocated space. -+ * @param mesh: mesh area. -+ * @return true if the query list is full. -+ * It checks the number of all queries, not just number of reply states, -+ * that have a client address. So that spawned queries count too, -+ * that were created by the iterator, or other modules. -+ */ -+int mesh_jostle_exceeded(struct mesh_area* mesh); -+ - #endif /* SERVICES_MESH_H */ --- -2.37.3 - diff --git a/SOURCES/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch b/SOURCES/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch deleted file mode 100644 index b2e1ca5..0000000 --- a/SOURCES/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch +++ /dev/null @@ -1,2304 +0,0 @@ -commit cc3259547f5f1c4a8206e169cf579d08f95d5178 -Author: Tomas Korbar -Date: Wed Feb 14 13:37:21 2024 +0100 - - Fix CVE-2023-50387 and CVE-2023-50868 - -diff --git a/unbound-1.16.2/services/authzone.c b/unbound-1.16.2/services/authzone.c -index b9e0b11..04527c3 100644 ---- a/unbound-1.16.2/services/authzone.c -+++ b/unbound-1.16.2/services/authzone.c -@@ -7766,6 +7766,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, - enum sec_status sec; - struct val_env* ve; - int m; -+ int verified = 0; - m = modstack_find(mods, "validator"); - if(m == -1) { - auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " -@@ -7789,7 +7790,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, - "zonemd: verify %s RRset with DNSKEY", typestr); - } - sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, -- LDNS_SECTION_ANSWER, NULL); -+ LDNS_SECTION_ANSWER, NULL, &verified); - if(sec == sec_status_secure) { - return 1; - } -diff --git a/unbound-1.16.2/services/cache/dns.c b/unbound-1.16.2/services/cache/dns.c -index b6e5697..0d75595 100644 ---- a/unbound-1.16.2/services/cache/dns.c -+++ b/unbound-1.16.2/services/cache/dns.c -@@ -695,6 +695,24 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, - return msg; - } - -+struct dns_msg* -+dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region) -+{ -+ size_t i; -+ struct dns_msg* res = NULL; -+ res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count); -+ if(!res) return NULL; -+ *res->rep = *origin->rep; -+ for(i=0; irep->rrset_count; i++) { -+ res->rep->rrsets[i] = packed_rrset_copy_region( -+ origin->rep->rrsets[i], region, 0); -+ if(!res->rep->rrsets[i]) { -+ return NULL; -+ } -+ } -+ return res; -+} -+ - /** synthesize RRset-only response from cached RRset item */ - static struct dns_msg* - rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, -diff --git a/unbound-1.16.2/services/cache/dns.h b/unbound-1.16.2/services/cache/dns.h -index 147f992..c2bf23c 100644 ---- a/unbound-1.16.2/services/cache/dns.h -+++ b/unbound-1.16.2/services/cache/dns.h -@@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q, - struct reply_info* r, struct regional* region, time_t now, - int allow_expired, struct regional* scratch); - -+/** -+ * Deep copy a dns_msg to a region. -+ * @param origin: the dns_msg to copy. -+ * @param region: the region to copy all the data to. -+ * @return the new dns_msg or NULL on malloc error. -+ */ -+struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin, -+ struct regional* region); -+ - /** - * Find cached message - * @param env: module environment with the DNS cache. -diff --git a/unbound-1.16.2/testcode/unitverify.c b/unbound-1.16.2/testcode/unitverify.c -index ff069a1..395b4c2 100644 ---- a/unbound-1.16.2/testcode/unitverify.c -+++ b/unbound-1.16.2/testcode/unitverify.c -@@ -180,6 +180,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, - enum sec_status sec; - char* reason = NULL; - uint8_t sigalg[ALGO_NEEDS_MAX+1]; -+ int verified = 0; - if(vsig) { - log_nametypeclass(VERB_QUERY, "verify of rrset", - rrset->rk.dname, ntohs(rrset->rk.type), -@@ -188,7 +189,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, - setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ - /* ok to give null as qstate here, won't be used for answer section. */ - sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, NULL, -- LDNS_SECTION_ANSWER, NULL); -+ LDNS_SECTION_ANSWER, NULL, &verified); - if(vsig) { - printf("verify outcome is: %s %s\n", sec_status_to_string(sec), - reason?reason:""); -@@ -442,9 +443,9 @@ nsec3_hash_test_entry(struct entry* e, rbtree_type* ct, - - ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname, - qinfo.qname_len, &hash); -- if(ret != 1) { -+ if(ret < 1) { - printf("Bad nsec3_hash_name retcode %d\n", ret); -- unit_assert(ret == 1); -+ unit_assert(ret == 1 || ret == 2); - } - unit_assert(hash->dname && hash->hash && hash->hash_len && - hash->b32 && hash->b32_len); -diff --git a/unbound-1.16.2/testdata/val_any.rpl b/unbound-1.16.2/testdata/val_any.rpl -index 4ce1951..90263af 100644 ---- a/unbound-1.16.2/testdata/val_any.rpl -+++ b/unbound-1.16.2/testdata/val_any.rpl -@@ -161,6 +161,9 @@ SECTION QUESTION - example.com. IN ANY - ENTRY_END - -+; Allow validation resuming for the RRSIGs -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/testdata/val_any_dname.rpl b/unbound-1.16.2/testdata/val_any_dname.rpl -index 6ab3cde..dd65e97 100644 ---- a/unbound-1.16.2/testdata/val_any_dname.rpl -+++ b/unbound-1.16.2/testdata/val_any_dname.rpl -@@ -163,6 +163,9 @@ SECTION QUESTION - example.com. IN ANY - ENTRY_END - -+; Allow validation resuming for the RRSIGs -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl b/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -index 8ff7e4b..87a55f5 100644 ---- a/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -+++ b/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -@@ -156,6 +156,9 @@ SECTION QUESTION - www.example.com. IN A - ENTRY_END - -+; Allow validation resuming for NSEC3 hash calculations -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/util/fptr_wlist.c b/unbound-1.16.2/util/fptr_wlist.c -index 05a22d4..5b198b2 100644 ---- a/unbound-1.16.2/util/fptr_wlist.c -+++ b/unbound-1.16.2/util/fptr_wlist.c -@@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) - else if(fptr == &pending_udp_timer_delay_cb) return 1; - else if(fptr == &worker_stat_timer_cb) return 1; - else if(fptr == &worker_probe_timer_cb) return 1; -+ else if(fptr == &validate_suspend_timer_cb) return 1; - #ifdef UB_ON_WINDOWS - else if(fptr == &wsvc_cron_cb) return 1; - #endif -diff --git a/unbound-1.16.2/validator/val_nsec.c b/unbound-1.16.2/validator/val_nsec.c -index 876bfab..5871db9 100644 ---- a/unbound-1.16.2/validator/val_nsec.c -+++ b/unbound-1.16.2/validator/val_nsec.c -@@ -180,6 +180,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, - { - struct packed_rrset_data* d = (struct packed_rrset_data*) - nsec->entry.data; -+ int verified = 0; - if(!d) return 0; - if(d->security == sec_status_secure) - return 1; -@@ -187,7 +188,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, - if(d->security == sec_status_secure) - return 1; - d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, -- NULL, LDNS_SECTION_AUTHORITY, qstate); -+ NULL, LDNS_SECTION_AUTHORITY, qstate, &verified); - if(d->security == sec_status_secure) { - rrset_update_sec_status(env->rrset_cache, nsec, *env->now); - return 1; -diff --git a/unbound-1.16.2/validator/val_nsec3.c b/unbound-1.16.2/validator/val_nsec3.c -index a2b3794..95d1e4d 100644 ---- a/unbound-1.16.2/validator/val_nsec3.c -+++ b/unbound-1.16.2/validator/val_nsec3.c -@@ -57,6 +57,19 @@ - /* we include nsec.h for the bitmap_has_type function */ - #include "validator/val_nsec.h" - #include "sldns/sbuffer.h" -+#include "util/config_file.h" -+ -+/** -+ * Max number of NSEC3 calculations at once, suspend query for later. -+ * 8 is low enough and allows for cases where multiple proofs are needed. -+ */ -+#define MAX_NSEC3_CALCULATIONS 8 -+/** -+ * When all allowed NSEC3 calculations at once resulted in error treat as -+ * bogus. NSEC3 hash errors are not cached and this helps breaks loops with -+ * erroneous data. -+ */ -+#define MAX_NSEC3_ERRORS -1 - - /** - * This function we get from ldns-compat or from base system -@@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2) - return memcmp(s1, s2, s1len); - } - -+int -+nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region) -+{ -+ if(ct->ct) return 1; -+ ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct)); -+ if(!ct->ct) return 0; -+ ct->region = region; -+ rbtree_init(ct->ct, &nsec3_hash_cmp); -+ return 1; -+} -+ - size_t - nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, - size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) -@@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, - c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); - if(c) { - *hash = c; -- return 1; -+ return 2; - } - /* create a new entry */ - c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); -@@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, - c->dname_len = dname_len; - r = nsec3_calc_hash(region, buf, c); - if(r != 1) -- return r; -+ return r; /* returns -1 or 0 */ - r = nsec3_calc_b32(region, buf, c); - if(r != 1) -- return r; -+ return r; /* returns 0 */ - #ifdef UNBOUND_DEBUG - n = - #else -@@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, - struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) - { - uint8_t* nm = s->rk.dname; -+ if(!hash) return 0; /* please clang */ - /* compare, does hash of name based on params in this NSEC3 - * match the owner name of this NSEC3? - * name must be: base32 . zone name -@@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, - * @param nmlen: length of name. - * @param rrset: nsec3 that matches is returned here. - * @param rr: rr number in nsec3 rrset that matches. -+ * @param calculations: current hash calculations. - * @return true if a matching NSEC3 is found, false if not. - */ - static int - find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, uint8_t* nm, size_t nmlen, -- struct ub_packed_rrset_key** rrset, int* rr) -+ struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, -+ struct ub_packed_rrset_key** rrset, int* rr, -+ int* calculations) - { - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash = NULL; - int r; -+ int calc_errors = 0; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { -+ /* check if we are allowed more calculations */ -+ if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ if(calc_errors == *calculations) { -+ *calculations = MAX_NSEC3_ERRORS; -+ } -+ break; -+ } - /* get name hashed for this NSEC3 RR */ -- r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, -+ r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ -- } else if(r != 1) -- continue; /* malformed NSEC3 */ -- else if(nsec3_hash_matches_owner(flt, hash, s)) { -- *rrset = s; /* rrset with this name */ -- *rr = i_rr; /* matches hash with these parameters */ -- return 1; -+ } else if(r < 0) { -+ /* malformed NSEC3 */ -+ calc_errors++; -+ (*calculations)++; -+ continue; -+ } else { -+ if(r == 1) (*calculations)++; -+ if(nsec3_hash_matches_owner(flt, hash, s)) { -+ *rrset = s; /* rrset with this name */ -+ *rr = i_rr; /* matches hash with these parameters */ -+ return 1; -+ } - } - } - *rrset = NULL; -@@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) - return 0; /* malformed RR proves nothing */ - -+ if(!hash) return 0; /* please clang */ - /* check the owner name is a hashed value . apex - * base32 encoded values must have equal length. - * hash_value and next hash value must have equal length. */ -@@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - * @param nmlen: length of name. - * @param rrset: covering NSEC3 rrset is returned here. - * @param rr: rr of cover is returned here. -+ * @param calculations: current hash calculations. - * @return true if a covering NSEC3 is found, false if not. - */ - static int - find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, uint8_t* nm, size_t nmlen, -- struct ub_packed_rrset_key** rrset, int* rr) -+ struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, -+ struct ub_packed_rrset_key** rrset, int* rr, -+ int* calculations) - { - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash = NULL; - int r; -+ int calc_errors = 0; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { -+ /* check if we are allowed more calculations */ -+ if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ if(calc_errors == *calculations) { -+ *calculations = MAX_NSEC3_ERRORS; -+ } -+ break; -+ } - /* get name hashed for this NSEC3 RR */ -- r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, -+ r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ -- } else if(r != 1) -- continue; /* malformed NSEC3 */ -- else if(nsec3_covers(flt->zone, hash, s, i_rr, -- env->scratch_buffer)) { -- *rrset = s; /* rrset with this name */ -- *rr = i_rr; /* covers hash with these parameters */ -- return 1; -+ } else if(r < 0) { -+ /* malformed NSEC3 */ -+ calc_errors++; -+ (*calculations)++; -+ continue; -+ } else { -+ if(r == 1) (*calculations)++; -+ if(nsec3_covers(flt->zone, hash, s, i_rr, -+ env->scratch_buffer)) { -+ *rrset = s; /* rrset with this name */ -+ *rr = i_rr; /* covers hash with these parameters */ -+ return 1; -+ } - } - } - *rrset = NULL; -@@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - * @param ct: cached hashes table. - * @param qinfo: query that is verified for. - * @param ce: closest encloser information is returned in here. -+ * @param calculations: current hash calculations. - * @return true if a closest encloser candidate is found, false if not. - */ - static int --nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) -+nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ struct ce_response* ce, int* calculations) - { - uint8_t* nm = qinfo->qname; - size_t nmlen = qinfo->qname_len; -@@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - * may be the case. */ - - while(dname_subdomain_c(nm, flt->zone)) { -+ if(*calculations >= MAX_NSEC3_CALCULATIONS || -+ *calculations == MAX_NSEC3_ERRORS) { -+ return 0; -+ } - if(find_matching_nsec3(env, flt, ct, nm, nmlen, -- &ce->ce_rrset, &ce->ce_rr)) { -+ &ce->ce_rrset, &ce->ce_rr, calculations)) { - ce->ce = nm; - ce->ce_len = nmlen; - return 1; -@@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, - * If set true, and the return value is true, then you can be - * certain that the ce.nc_rrset and ce.nc_rr are set properly. - * @param ce: closest encloser information is returned in here. -+ * @param calculations: pointer to the current NSEC3 hash calculations. - * @return bogus if no closest encloser could be proven. - * secure if a closest encloser could be proven, ce is set. - * insecure if the closest-encloser candidate turns out to prove - * that an insecure delegation exists above the qname. -+ * unchecked if no more hash calculations are allowed at this point. - */ - static enum sec_status --nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, -- struct ce_response* ce) -+nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ int prove_does_not_exist, struct ce_response* ce, int* calculations) - { - uint8_t* nc; - size_t nc_len; - /* robust: clean out ce, in case it gets abused later */ - memset(ce, 0, sizeof(*ce)); - -- if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { -+ if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) { -+ if(*calculations == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " -+ "not find a candidate for the closest " -+ "encloser; all attempted hash calculations " -+ "were erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " -+ "not find a candidate for the closest " -+ "encloser; reached MAX_NSEC3_CALCULATIONS " -+ "(%d); unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " - "not find a candidate for the closest encloser."); - return sec_status_bogus; -@@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - /* Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); - if(!find_covering_nsec3(env, flt, ct, nc, nc_len, -- &ce->nc_rrset, &ce->nc_rr)) { -+ &ce->nc_rrset, &ce->nc_rr, calculations)) { -+ if(*calculations == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3: Could not find proof that the " -+ "candidate encloser was the closest encloser; " -+ "all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3: Could not find proof that the " -+ "candidate encloser was the closest encloser; " -+ "reached MAX_NSEC3_CALCULATIONS (%d); " -+ "unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3: Could not find proof that the " -- "candidate encloser was the closest encloser"); -+ "candidate encloser was the closest encloser"); - return sec_status_bogus; - } - return sec_status_secure; -@@ -1019,8 +1113,8 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, - - /** Do the name error proof */ - static enum sec_status --nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo) -+nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc) - { - struct ce_response ce; - uint8_t* wc; -@@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - /* First locate and prove the closest encloser to qname. We will - * use the variant that fails if the closest encloser turns out - * to be qname. */ -- sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); -+ sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); - if(sec != sec_status_secure) { - if(sec == sec_status_bogus) - verbose(VERB_ALGO, "nsec3 nameerror proof: failed " - "to prove a closest encloser"); -+ else if(sec == sec_status_unchecked) -+ verbose(VERB_ALGO, "nsec3 nameerror proof: will " -+ "continue proving closest encloser after " -+ "suspend"); - else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " - "nsec3 is an insecure delegation"); - return sec; -@@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - /* At this point, we know that qname does not exist. Now we need - * to prove that the wildcard does not exist. */ - log_assert(ce.ce); -- wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); -- if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, -- &wc_rrset, &wc_rr)) { -+ wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); -+ if(!wc) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist."); -+ return sec_status_bogus; -+ } -+ if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) { -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist; " -+ "all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist; " -+ "reached MAX_NSEC3_CALCULATIONS (%d); " -+ "unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " - "that the applicable wildcard did not exist."); - return sec_status_bogus; -@@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - enum sec_status - nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey) -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - return sec_status_insecure; /* iteration count too high */ - log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", - flt.zone, 0, 0); -- return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); -+ return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); - } - - /* -@@ -1089,8 +1204,9 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - - /** Do the nodata proof */ - static enum sec_status --nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo) -+nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ int* calc) - { - struct ce_response ce; - uint8_t* wc; -@@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - enum sec_status sec; - - if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, -- &rrset, &rr)) { -+ &rrset, &rr, calc)) { - /* cases 1 and 2 */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " -@@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - } - return sec_status_secure; - } -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "proveNodata: all attempted hash " -+ "calculations were erroneous while finding a matching " -+ "NSEC3, bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "proveNodata: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while finding a " -+ "matching NSEC3; unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* For cases 3 - 5, we need the proven closest encloser, and it - * can't match qname. Although, at this point, we know that it - * won't since we just checked that. */ -- sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); -+ sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); - if(sec == sec_status_bogus) { - verbose(VERB_ALGO, "proveNodata: did not match qname, " - "nor found a proven closest encloser."); -@@ -1157,14 +1285,17 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " - "delegation."); - return sec_status_insecure; -+ } else if(sec==sec_status_unchecked) { -+ return sec_status_unchecked; - } - - /* Case 3: removed */ - - /* Case 4: */ - log_assert(ce.ce); -- wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); -- if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { -+ wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); -+ if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr, -+ calc)) { - /* found wildcard */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " -@@ -1195,6 +1326,18 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - } - return sec_status_secure; - } -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash " -+ "calculations were erroneous while matching " -+ "wildcard, bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 nodata proof: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while matching " -+ "wildcard, unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* Case 5: */ - /* Due to forwarders, cnames, and other collating effects, we -@@ -1223,28 +1366,27 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - enum sec_status - nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey) -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ -- return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); -+ return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); - } - - enum sec_status - nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) -+ struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - uint8_t* nc; -@@ -1254,7 +1396,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1272,8 +1413,22 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - /* Now we still need to prove that the original data did not exist. - * Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); -- if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, -- &ce.nc_rrset, &ce.nc_rr)) { -+ if(!find_covering_nsec3(env, &flt, ct, nc, nc_len, -+ &ce.nc_rrset, &ce.nc_rr, calc)) { -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "proveWildcard: did not find a " -+ "covering NSEC3 that covered the next closer " -+ "name; all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "proveWildcard: did not find a " -+ "covering NSEC3 that covered the next closer " -+ "name; reached MAX_NSEC3_CALCULATIONS " -+ "(%d); unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "proveWildcard: did not find a covering " - "NSEC3 that covered the next closer name."); - return sec_status_bogus; -@@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, - { - struct packed_rrset_data* d; - size_t i; -+ int verified = 0; - for(i=0; ientry.data; - if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) -@@ -1304,7 +1460,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, - if(d->security == sec_status_secure) - continue; - d->security = val_verify_rrset_entry(env, ve, list[i], kkey, -- reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); -+ reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, -+ &verified); - if(d->security != sec_status_secure) { - verbose(VERB_ALGO, "NSEC3 did not verify"); - return 0; -@@ -1318,13 +1475,16 @@ enum sec_status - nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason, -- sldns_ede_code* reason_bogus, struct module_qstate* qstate) -+ sldns_ede_code* reason_bogus, struct module_qstate* qstate, -+ struct nsec3_cache_table* ct) - { -- rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - struct ub_packed_rrset_key* rrset; - int rr; -+ int calc = 0; -+ enum sec_status sec; -+ - log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { -@@ -1335,7 +1495,6 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - *reason = "not all NSEC3 records secure"; - return sec_status_bogus; /* not all NSEC3 records secure */ - } -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) { - *reason = "no NSEC3 records"; -@@ -1346,8 +1505,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - - /* Look for a matching NSEC3 to qname -- this is the normal - * NODATA case. */ -- if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, -- &rrset, &rr)) { -+ if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len, -+ &rrset, &rr, &calc)) { - /* If the matching NSEC3 has the SOA bit set, it is from - * the wrong zone (the child instead of the parent). If - * it has the DS bit set, then we were lied to. */ -@@ -1370,10 +1529,24 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - /* Otherwise, this proves no DS. */ - return sec_status_secure; - } -+ if(calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 provenods: all attempted hash " -+ "calculations were erroneous while finding a matching " -+ "NSEC3, bogus"); -+ return sec_status_bogus; -+ } else if(calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 provenods: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while finding a " -+ "matching NSEC3, unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* Otherwise, we are probably in the opt-out case. */ -- if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) -- != sec_status_secure) { -+ sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc); -+ if(sec == sec_status_unchecked) { -+ return sec_status_unchecked; -+ } else if(sec != sec_status_secure) { - /* an insecure delegation *above* the qname does not prove - * anything about this qname exactly, and bogus is bogus */ - verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " -@@ -1407,17 +1580,16 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - - enum sec_status - nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, -- struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) -+ struct ub_packed_rrset_key** list, size_t num, -+ struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, -+ struct nsec3_cache_table* ct, int* calc) - { - enum sec_status sec, secnx; -- rbtree_type ct; - struct nsec3_filter flt; - *nodata = 0; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1427,16 +1599,20 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - /* try nxdomain and nodata after another, while keeping the - * hash cache intact */ - -- secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); -+ secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); - if(secnx==sec_status_secure) - return sec_status_secure; -- sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); -+ else if(secnx == sec_status_unchecked) -+ return sec_status_unchecked; -+ sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); - if(sec==sec_status_secure) { - *nodata = 1; - } else if(sec == sec_status_insecure) { - *nodata = 1; - } else if(secnx == sec_status_insecure) { - sec = sec_status_insecure; -+ } else if(sec == sec_status_unchecked) { -+ return sec_status_unchecked; - } - return sec; - } -diff --git a/unbound-1.16.2/validator/val_nsec3.h b/unbound-1.16.2/validator/val_nsec3.h -index 7676fc8..8ca9129 100644 ---- a/unbound-1.16.2/validator/val_nsec3.h -+++ b/unbound-1.16.2/validator/val_nsec3.h -@@ -98,6 +98,15 @@ struct sldns_buffer; - /** The SHA1 hash algorithm for NSEC3 */ - #define NSEC3_HASH_SHA1 0x01 - -+/** -+* Cache table for NSEC3 hashes. -+* It keeps a *pointer* to the region its items are allocated. -+*/ -+struct nsec3_cache_table { -+ rbtree_type* ct; -+ struct regional* region; -+}; -+ - /** - * Determine if the set of NSEC3 records provided with a response prove NAME - * ERROR. This means that the NSEC3s prove a) the closest encloser exists, -@@ -110,14 +119,18 @@ struct sldns_buffer; - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the Name Error is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey); -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA -@@ -144,15 +157,18 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey); -- -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Prove that a positive wildcard match was appropriate (no direct match -@@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - * @param kkey: key entry that signed the NSEC3s. - * @param wc: The purported wildcard that matched. This is the wildcard name - * as *.wildcard.name., with the *. label already removed. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); -+ struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Prove that a DS response either had no DS, or wasn't a delegation point. -@@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - * @param reason: string for bogus result. - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param qstate: qstate with region. -+ * @param ct: cached hashes table. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - * or if there was no DS in an insecure (i.e., opt-in) way, -- * INDETERMINATE if it was clear that this wasn't a delegation point. -+ * INDETERMINATE if it was clear that this wasn't a delegation point, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason, -- sldns_ede_code* reason_bogus, struct module_qstate* qstate); -+ sldns_ede_code* reason_bogus, struct module_qstate* qstate, -+ struct nsec3_cache_table* ct); - - /** - * Prove NXDOMAIN or NODATA. -@@ -212,14 +235,18 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - * @param kkey: key entry that signed the NSEC3s. - * @param nodata: if return value is secure, this indicates if nodata or - * nxdomain was proven. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); -+ struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * The NSEC3 hash result storage. -@@ -256,6 +283,14 @@ struct nsec3_cached_hash { - */ - int nsec3_hash_cmp(const void* c1, const void* c2); - -+/** -+ * Initialise the NSEC3 cache table. -+ * @param ct: the nsec3 cache table. -+ * @param region: the region where allocations for the table will happen. -+ * @return true on success, false on malloc error. -+ */ -+int nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region); -+ - /** - * Obtain the hash of an owner name. - * Used internally by the nsec3 proof functions in this file. -@@ -272,7 +307,8 @@ int nsec3_hash_cmp(const void* c1, const void* c2); - * @param dname_len: the length of the name. - * @param hash: the hash node is returned on success. - * @return: -- * 1 on success, either from cache or newly hashed hash is returned. -+ * 2 on success, hash from cache is returned. -+ * 1 on success, newly computed hash is returned. - * 0 on a malloc failure. - * -1 if the NSEC3 rr was badly formatted (i.e. formerr). - */ -diff --git a/unbound-1.16.2/validator/val_sigcrypt.c b/unbound-1.16.2/validator/val_sigcrypt.c -index 5ab21e2..8600a68 100644 ---- a/unbound-1.16.2/validator/val_sigcrypt.c -+++ b/unbound-1.16.2/validator/val_sigcrypt.c -@@ -78,6 +78,9 @@ - #include - #endif - -+/** Maximum number of RRSIG validations for an RRset. */ -+#define MAX_VALIDATE_RRSIGS 8 -+ - /** return number of rrs in an rrset */ - static size_t - rrset_get_count(struct ub_packed_rrset_key* rrset) -@@ -541,6 +544,8 @@ int algo_needs_missing(struct algo_needs* n) - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param numverified: incremented when the number of RRSIG validations -+ * increases. - * @return secure if any key signs *this* signature. bogus if no key signs it, - * unchecked on error, or indeterminate if all keys are not supported by - * the crypto library (openssl3+ only). -@@ -551,7 +556,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* numverified) - { - /* find matching keys and check them */ - enum sec_status sec = sec_status_bogus; -@@ -575,6 +581,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - tag != dnskey_calc_keytag(dnskey, i)) - continue; - numchecked ++; -+ (*numverified)++; - - /* see if key verifies */ - sec = dnskey_verify_rrset_sig(env->scratch, -@@ -585,6 +592,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - return sec; - else if(sec == sec_status_indeterminate) - numindeterminate ++; -+ if(*numverified > MAX_VALIDATE_RRSIGS) { -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ verbose(VERB_ALGO, "verify sig: too many RRSIG validations"); -+ return sec_status_bogus; -+ } - } - if(numchecked == 0) { - *reason = "signatures from unknown keys"; -@@ -608,7 +622,7 @@ enum sec_status - dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, int* verified) - { - enum sec_status sec; - size_t i, num; -@@ -616,6 +630,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - /* make sure that for all DNSKEY algorithms there are valid sigs */ - struct algo_needs needs; - int alg; -+ *verified = 0; - - num = rrset_get_sigcount(rrset); - if(num == 0) { -@@ -640,7 +655,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - for(i=0; inow, rrset, - dnskey, i, &sortree, reason, reason_bogus, -- section, qstate); -+ section, qstate, verified); - /* see which algorithm has been fixed up */ - if(sec == sec_status_secure) { - if(!sigalg) -@@ -652,6 +667,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - algo_needs_set_bogus(&needs, - (uint8_t)rrset_get_sig_algo(rrset, i)); - } -+ if(*verified > MAX_VALIDATE_RRSIGS) { -+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ return sec_status_bogus; -+ } - } - if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - verbose(VERB_ALGO, "rrset failed to verify: " -@@ -690,6 +712,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, - int buf_canon = 0; - uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); - int algo = dnskey_get_algo(dnskey, dnskey_idx); -+ int numverified = 0; - - num = rrset_get_sigcount(rrset); - if(num == 0) { -@@ -713,8 +736,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, - if(sec == sec_status_secure) - return sec; - numchecked ++; -+ numverified ++; - if(sec == sec_status_indeterminate) - numindeterminate ++; -+ if(numverified > MAX_VALIDATE_RRSIGS) { -+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ return sec_status_bogus; -+ } - } - verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); - if(!numchecked) { -diff --git a/unbound-1.16.2/validator/val_sigcrypt.h b/unbound-1.16.2/validator/val_sigcrypt.h -index 7f52b71..1a3d8fc 100644 ---- a/unbound-1.16.2/validator/val_sigcrypt.h -+++ b/unbound-1.16.2/validator/val_sigcrypt.h -@@ -260,6 +260,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param verified: if not NULL the number of RRSIG validations is returned. - * @return SECURE if one key in the set verifies one rrsig. - * UNCHECKED on allocation errors, unsupported algorithms, malformed data, - * and BOGUS on verification failures (no keys match any signatures). -@@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate); -+ sldns_pkt_section section, struct module_qstate* qstate, int* verified); - - - /** -diff --git a/unbound-1.16.2/validator/val_utils.c b/unbound-1.16.2/validator/val_utils.c -index e2319ee..cb37ea0 100644 ---- a/unbound-1.16.2/validator/val_utils.c -+++ b/unbound-1.16.2/validator/val_utils.c -@@ -58,6 +58,10 @@ - #include "sldns/wire2str.h" - #include "sldns/parseutil.h" - -+/** Maximum allowed digest match failures per DS, for DNSKEYs with the same -+ * properties */ -+#define MAX_DS_MATCH_FAILURES 4 -+ - enum val_classification - val_classify_response(uint16_t query_flags, struct query_info* origqinf, - struct query_info* qinf, struct reply_info* rep, size_t skip) -@@ -336,7 +340,8 @@ static enum sec_status - val_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int *verified) - { - enum sec_status sec; - struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> -@@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, - log_nametypeclass(VERB_ALGO, "verify rrset cached", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); -+ *verified = 0; - return d->security; - } - /* check in the cache if verification has already been done */ -@@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, - log_nametypeclass(VERB_ALGO, "verify rrset from cache", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); -+ *verified = 0; - return d->security; - } - log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, - ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, -- reason_bogus, section, qstate); -+ reason_bogus, section, qstate, verified); - verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); - regional_free_all(env->scratch); - -@@ -393,7 +400,8 @@ enum sec_status - val_verify_rrset_entry(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* verified) - { - /* temporary dnskey rrset-key */ - struct ub_packed_rrset_key dnskey; -@@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, - dnskey.entry.key = &dnskey; - dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, -- reason_bogus, section, qstate); -+ reason_bogus, section, qstate, verified); - return sec; - } - -@@ -439,6 +447,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, - if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, - ds_idx)) { - verbose(VERB_ALGO, "DS match attempt failed"); -+ if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) { -+ verbose(VERB_ALGO, "DS match attempt reached " -+ "MAX_DS_MATCH_FAILURES (%d); bogus", -+ MAX_DS_MATCH_FAILURES); -+ return sec_status_bogus; -+ } - continue; - } - numhashok++; -diff --git a/unbound-1.16.2/validator/val_utils.h b/unbound-1.16.2/validator/val_utils.h -index 83e3d0a..e8cdcef 100644 ---- a/unbound-1.16.2/validator/val_utils.h -+++ b/unbound-1.16.2/validator/val_utils.h -@@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype, - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param verified: if not NULL, the number of RRSIG validations is returned. - * @return security status of verification. - */ - enum sec_status val_verify_rrset_entry(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate); -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* verified); - - /** - * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but -diff --git a/unbound-1.16.2/validator/validator.c b/unbound-1.16.2/validator/validator.c -index 1723afe..01303a8 100644 ---- a/unbound-1.16.2/validator/validator.c -+++ b/unbound-1.16.2/validator/validator.c -@@ -64,10 +64,15 @@ - #include "sldns/wire2str.h" - #include "sldns/str2wire.h" - -+/** Max number of RRSIGs to validate at once, suspend query for later. */ -+#define MAX_VALIDATE_AT_ONCE 8 -+/** Max number of validation suspends allowed, error out otherwise. */ -+#define MAX_VALIDATION_SUSPENDS 16 -+ - /* forward decl for cache response and normal super inform calls of a DS */ - static void process_ds_response(struct module_qstate* qstate, - struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, -- struct query_info* qinfo, struct sock_list* origin); -+ struct query_info* qinfo, struct sock_list* origin, int* suspend); - - - /* Updates the suplied EDE (RFC8914) code selectively so we don't loose -@@ -281,6 +286,21 @@ val_new(struct module_qstate* qstate, int id) - return val_new_getmsg(qstate, vq); - } - -+/** reset validator query state for query restart */ -+static void -+val_restart(struct val_qstate* vq) -+{ -+ struct comm_timer* temp_timer; -+ int restart_count; -+ if(!vq) return; -+ temp_timer = vq->suspend_timer; -+ restart_count = vq->restart_count+1; -+ memset(vq, 0, sizeof(*vq)); -+ vq->suspend_timer = temp_timer; -+ vq->restart_count = restart_count; -+ vq->state = VAL_INIT_STATE; -+} -+ - /** - * Exit validation with an error status - * -@@ -587,30 +607,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, - * completed. - * - * @param qstate: query state. -+ * @param vq: validator query state. - * @param env: module env for verify. - * @param ve: validator env for verify. - * @param qchase: query that was made. - * @param chase_reply: answer to validate. - * @param key_entry: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - * @return false if any of the rrsets in the an or ns sections of the message - * fail to verify. The message is then set to bogus. - */ - static int --validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, -- struct val_env* ve, struct query_info* qchase, -- struct reply_info* chase_reply, struct key_entry_key* key_entry) -+validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, -+ struct module_env* env, struct val_env* ve, struct query_info* qchase, -+ struct reply_info* chase_reply, struct key_entry_key* key_entry, -+ int* suspend) - { - uint8_t* sname; - size_t i, slen; - struct ub_packed_rrset_key* s; - enum sec_status sec; -- int dname_seen = 0; -+ int dname_seen = 0, num_verifies = 0, verified, have_state = 0; - char* reason = NULL; - sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ *suspend = 0; -+ if(vq->msg_signatures_state) { -+ /* Pick up the state, and reset it, may not be needed now. */ -+ vq->msg_signatures_state = 0; -+ have_state = 1; -+ } - - /* validate the ANSWER section */ - for(i=0; ian_numrrsets; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - /* Skip the CNAME following a (validated) DNAME. - * Because of the normalization routines in the iterator, -@@ -629,7 +661,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - - /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, -- &reason_bogus, LDNS_SECTION_ANSWER, qstate); -+ &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); - /* If the (answer) rrset failed to validate, then this - * message is BAD. */ - if(sec != sec_status_secure) { -@@ -654,14 +686,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { - dname_seen = 1; - } -+ num_verifies += verified; -+ if(num_verifies > MAX_VALIDATE_AT_ONCE && -+ i+1 < (env->cfg->val_clean_additional? -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets: -+ chase_reply->rrset_count)) { -+ /* If the number of RRSIGs exceeds the maximum in -+ * one go, suspend. Only suspend if there is a next -+ * rrset to verify, i+1msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - /* validate the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, -- &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); -+ &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, -+ &verified); - /* If anything in the authority section fails to be secure, - * we have a bad message. */ - if(sec != sec_status_secure) { -@@ -675,6 +726,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - update_reason_bogus(chase_reply, reason_bogus); - return 0; - } -+ num_verifies += verified; -+ if(num_verifies > MAX_VALIDATE_AT_ONCE && -+ i+1 < (env->cfg->val_clean_additional? -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets: -+ chase_reply->rrset_count)) { -+ *suspend = 1; -+ vq->msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - /* If set, the validator should clean the additional section of -@@ -684,22 +747,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - /* attempt to validate the ADDITIONAL section rrsets */ - for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; - irrset_count; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - /* only validate rrs that have signatures with the key */ - /* leave others unchecked, those get removed later on too */ - val_find_rrset_signer(s, &sname, &slen); - -+ verified = 0; - if(sname && query_dname_compare(sname, key_entry->name)==0) - (void)val_verify_rrset_entry(env, ve, s, key_entry, -- &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); -+ &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, -+ &verified); - /* the additional section can fail to be secure, - * it is optional, check signature in case we need - * to clean the additional section later. */ -+ num_verifies += verified; -+ if(num_verifies > MAX_VALIDATE_AT_ONCE && -+ i+1 < chase_reply->rrset_count) { -+ *suspend = 1; -+ vq->msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - return 1; - } - -+void -+validate_suspend_timer_cb(void* arg) -+{ -+ struct module_qstate* qstate = (struct module_qstate*)arg; -+ verbose(VERB_ALGO, "validate_suspend timer, continue"); -+ mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, -+ NULL); -+} -+ -+/** Setup timer to continue validation of msg signatures later */ -+static int -+validate_suspend_setup_timer(struct module_qstate* qstate, -+ struct val_qstate* vq, int id, enum val_state resume_state) -+{ -+ struct timeval tv; -+ int usec, slack, base; -+ if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { -+ verbose(VERB_ALGO, "validate_suspend timer: " -+ "reached MAX_VALIDATION_SUSPENDS (%d); error out", -+ MAX_VALIDATION_SUSPENDS); -+ errinf(qstate, "max validation suspends reached, " -+ "too many RRSIG validations"); -+ return 0; -+ } -+ verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); -+ vq->state = resume_state; -+ qstate->ext_state[id] = module_wait_reply; -+ if(!vq->suspend_timer) { -+ vq->suspend_timer = comm_timer_create( -+ qstate->env->worker_base, -+ validate_suspend_timer_cb, qstate); -+ if(!vq->suspend_timer) { -+ log_err("validate_suspend_setup_timer: " -+ "out of memory for comm_timer_create"); -+ return 0; -+ } -+ } -+ /* The timer is activated later, after other events in the event -+ * loop have been processed. The query state can also be deleted, -+ * when the list is full and query states are dropped. */ -+ /* Extend wait time if there are a lot of queries or if this one -+ * is taking long, to keep around cpu time for ordinary queries. */ -+ usec = 50000; /* 50 msec */ -+ slack = 0; -+ if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) -+ slack += 3; -+ else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) -+ slack += 2; -+ else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) -+ slack += 1; -+ if(vq->suspend_count > 3) -+ slack += 3; -+ else if(vq->suspend_count > 0) -+ slack += vq->suspend_count; -+ if(slack != 0 && slack <= 12 /* No numeric overflow. */) { -+ usec = usec << slack; -+ } -+ /* Spread such timeouts within 90%-100% of the original timer. */ -+ base = usec * 9/10; -+ usec = base + ub_random_max(qstate->env->rnd, usec-base); -+ tv.tv_usec = (usec % 1000000); -+ tv.tv_sec = (usec / 1000000); -+ vq->suspend_count ++; -+ comm_timer_set(vq->suspend_timer, &tv); -+ return 1; -+} -+ - /** - * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding - * and saw the NS record without signatures from a referral). -@@ -798,11 +942,17 @@ remove_spurious_authority(struct reply_info* chase_reply, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_positive_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - uint8_t* wc = NULL; - size_t wl; -@@ -811,6 +961,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - /* validate the ANSWER section - this will be the answer itself */ - for(i=0; ian_numrrsets; i++) { -@@ -862,17 +1013,23 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, wc); -+ chase_reply->ns_numrrsets, qchase, kkey, wc, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -904,11 +1061,17 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_nodata_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - /* Since we are here, there must be nothing in the ANSWER section to - * validate. */ -@@ -925,6 +1088,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; /* nsec3s seen */ - struct ub_packed_rrset_key* s; - size_t i; -+ *suspend = 0; - - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -@@ -963,16 +1127,23 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - } - } - -- if(!has_valid_nsec && nsec3s_seen) { -+ if(!has_valid_nsec && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - enum sec_status sec = nsec3_prove_nodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey); -+ chase_reply->ns_numrrsets, qchase, kkey, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "NODATA response is insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - has_valid_nsec = 1; -+ } else if(sec == sec_status_unchecked) { -+ /* check is incomplete; suspend */ -+ *suspend = 1; -+ return; -+ } - } - - if(!has_valid_nsec) { -@@ -1004,11 +1175,18 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_nameerror_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey, int* rcode) -+ struct key_entry_key* kkey, int* rcode, -+ struct module_qstate* qstate, struct val_qstate* vq, -+ int* nsec3_calculations, int* suspend) - { - int has_valid_nsec = 0; - int has_valid_wnsec = 0; -@@ -1018,6 +1196,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - uint8_t* ce; - int ce_labs = 0; - int prev_ce_labs = 0; -+ *suspend = 0; - - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -@@ -1047,13 +1226,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - nsec3s_seen = 1; - } - -- if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { -+ if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - /* use NSEC3 proof, both answer and auth rrsets, in case - * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ - chase_reply->security = nsec3_prove_nameerror(env, ve, - chase_reply->rrsets, chase_reply->an_numrrsets+ -- chase_reply->ns_numrrsets, qchase, kkey); -- if(chase_reply->security != sec_status_secure) { -+ chase_reply->ns_numrrsets, qchase, kkey, -+ &vq->nsec3_cache_table, nsec3_calculations); -+ if(chase_reply->security == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } else if(chase_reply->security != sec_status_secure) { - verbose(VERB_QUERY, "NameError response failed nsec, " - "nsec3 proof was %s", sec_status_to_string( - chase_reply->security)); -@@ -1065,26 +1249,34 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - - /* If the message fails to prove either condition, it is bogus. */ - if(!has_valid_nsec) { -+ validate_nodata_response(env, ve, qchase, chase_reply, kkey, -+ qstate, vq, nsec3_calculations, suspend); -+ if(*suspend) return; - verbose(VERB_QUERY, "NameError response has failed to prove: " - "qname does not exist"); -- chase_reply->security = sec_status_bogus; -- update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - /* Be lenient with RCODE in NSEC NameError responses */ -- validate_nodata_response(env, ve, qchase, chase_reply, kkey); -- if (chase_reply->security == sec_status_secure) -+ if(chase_reply->security == sec_status_secure) { - *rcode = LDNS_RCODE_NOERROR; -+ } else { -+ chase_reply->security = sec_status_bogus; -+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); -+ } - return; - } - - if(!has_valid_wnsec) { -+ validate_nodata_response(env, ve, qchase, chase_reply, kkey, -+ qstate, vq, nsec3_calculations, suspend); -+ if(*suspend) return; - verbose(VERB_QUERY, "NameError response has failed to prove: " - "covering wildcard does not exist"); -- chase_reply->security = sec_status_bogus; -- update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - /* Be lenient with RCODE in NSEC NameError responses */ -- validate_nodata_response(env, ve, qchase, chase_reply, kkey); -- if (chase_reply->security == sec_status_secure) -+ if (chase_reply->security == sec_status_secure) { - *rcode = LDNS_RCODE_NOERROR; -+ } else { -+ chase_reply->security = sec_status_bogus; -+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); -+ } - return; - } - -@@ -1144,11 +1336,17 @@ validate_referral_response(struct reply_info* chase_reply) - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_any_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - /* all answer and auth rrsets already verified */ - /* but check if a wildcard response is given, then check NSEC/NSEC3 -@@ -1159,6 +1357,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - if(qchase->qtype != LDNS_RR_TYPE_ANY) { - log_err("internal error: ANY validation called for non-ANY"); -@@ -1213,19 +1412,25 @@ validate_any_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - /* look both in answer and auth section for NSEC3s */ -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets, -- chase_reply->an_numrrsets+chase_reply->ns_numrrsets, -- qchase, kkey, wc); -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets, -+ qchase, kkey, wc, &vq->nsec3_cache_table, -+ nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive ANY wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -1258,11 +1463,17 @@ validate_any_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_cname_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - uint8_t* wc = NULL; - size_t wl; -@@ -1270,6 +1481,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - /* validate the ANSWER section - this will be the CNAME (+DNAME) */ - for(i=0; ian_numrrsets; i++) { -@@ -1334,17 +1546,23 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, wc); -+ chase_reply->ns_numrrsets, qchase, kkey, wc, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "wildcard CNAME response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -1375,11 +1593,17 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ - uint8_t* ce = NULL; /* for wildcard nodata responses. This is the -@@ -1393,6 +1617,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ - int ce_labs = 0; - int prev_ce_labs = 0; -+ *suspend = 0; - - /* the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; ian_numrrsets+ -@@ -1458,11 +1683,13 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - return; - } -- if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { -+ if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - int nodata; - enum sec_status sec = nsec3_prove_nxornodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, &nodata); -+ chase_reply->ns_numrrsets, qchase, kkey, &nodata, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "CNAMEchain to noanswer response " - "is insecure"); -@@ -1472,6 +1699,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - if(nodata) - nodata_valid_nsec = 1; - else nxdomain_valid_nsec = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; - } - } - -@@ -1822,13 +2052,37 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) - * Uses negative cache for NSEC3 lookup of DS responses. */ - /* only if cache not blacklisted, of course */ - struct dns_msg* msg; -- if(!qstate->blacklist && !vq->chain_blacklist && -+ int suspend; -+ if(vq->sub_ds_msg) { -+ /* We have a suspended DS reply from a sub-query; -+ * process it. */ -+ verbose(VERB_ALGO, "Process suspended sub DS response"); -+ msg = vq->sub_ds_msg; -+ process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, -+ msg, &msg->qinfo, NULL, &suspend); -+ if(suspend) { -+ /* we'll come back here later to continue */ -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_FINDKEY_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } -+ vq->sub_ds_msg = NULL; -+ return 1; /* continue processing ds-response results */ -+ } else if(!qstate->blacklist && !vq->chain_blacklist && - (msg=val_find_DS(qstate->env, target_key_name, - target_key_len, vq->qchase.qclass, qstate->region, - vq->key_entry->name)) ) { - verbose(VERB_ALGO, "Process cached DS response"); - process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, -- msg, &msg->qinfo, NULL); -+ msg, &msg->qinfo, NULL, &suspend); -+ if(suspend) { -+ /* we'll come back here later to continue */ -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_FINDKEY_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - return 1; /* continue processing ds-response results */ - } - if(!generate_request(qstate, id, target_key_name, -@@ -1871,7 +2125,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) - { - enum val_classification subtype; -- int rcode; -+ int rcode, suspend, nsec3_calculations = 0; - - if(!vq->key_entry) { - verbose(VERB_ALGO, "validate: no key entry, failed"); -@@ -1926,8 +2180,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - - /* check signatures in the message; - * answer and authority must be valid, additional is only checked. */ -- if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, -- vq->chase_reply, vq->key_entry)) { -+ if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, -+ vq->chase_reply, vq->key_entry, &suspend)) { -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - /* workaround bad recursor out there that truncates (even - * with EDNS4k) to 512 by removing RRSIG from auth section - * for positive replies*/ -@@ -1956,7 +2216,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_POSITIVE: - verbose(VERB_ALGO, "Validating a positive response"); - validate_positive_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(positive): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1965,7 +2232,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_NODATA: - verbose(VERB_ALGO, "Validating a nodata response"); - validate_nodata_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(nodata): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1975,7 +2249,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); - verbose(VERB_ALGO, "Validating a nxdomain response"); - validate_nameerror_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry, &rcode); -+ &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(nxdomain): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1986,7 +2267,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_CNAME: - verbose(VERB_ALGO, "Validating a cname response"); - validate_cname_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(cname): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1996,7 +2284,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - verbose(VERB_ALGO, "Validating a cname noanswer " - "response"); - validate_cname_noanswer_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(cname_noanswer): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -2013,8 +2308,15 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_ANY: - verbose(VERB_ALGO, "Validating a positive ANY " - "response"); -- validate_any_response(qstate->env, ve, &vq->qchase, -- vq->chase_reply, vq->key_entry); -+ validate_any_response(qstate->env, ve, &vq->qchase, -+ vq->chase_reply, vq->key_entry, qstate, vq, -+ &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(positive_any): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -2123,16 +2425,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, - if(vq->orig_msg->rep->security == sec_status_bogus) { - /* see if we can try again to fetch data */ - if(vq->restart_count < ve->max_restart) { -- int restart_count = vq->restart_count+1; - verbose(VERB_ALGO, "validation failed, " - "blacklist and retry to fetch data"); - val_blacklist(&qstate->blacklist, qstate->region, - qstate->reply_origin, 0); - qstate->reply_origin = NULL; - qstate->errinf = NULL; -- memset(vq, 0, sizeof(*vq)); -- vq->restart_count = restart_count; -- vq->state = VAL_INIT_STATE; -+ val_restart(vq); - verbose(VERB_ALGO, "pass back to next module"); - qstate->ext_state[id] = module_restart_next; - return 0; -@@ -2440,7 +2739,10 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, - * DS response indicated an end to secure space, is_good if the DS - * validated. It returns ke=NULL if the DS response indicated that the - * request wasn't a delegation point. -- * @return 0 on servfail error (malloc failure). -+ * @return -+ * 0 on success, -+ * 1 on servfail error (malloc failure), -+ * 2 on NSEC3 suspend. - */ - static int - ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, -@@ -2451,6 +2753,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - char* reason = NULL; - sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; - enum val_classification subtype; -+ int verified; - if(rcode != LDNS_RCODE_NOERROR) { - char rc[16]; - rc[0]=0; -@@ -2479,7 +2782,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - /* Verify only returns BOGUS or SECURE. If the rrset is - * bogus, then we are done. */ - sec = val_verify_rrset_entry(qstate->env, ve, ds, -- vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); -+ vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); - if(sec != sec_status_secure) { - verbose(VERB_DETAIL, "DS rrset in DS response did " - "not verify"); -@@ -2499,7 +2802,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - *ke = key_entry_create_null(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, - ub_packed_rrset_ttl(ds), *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } - - /* Otherwise, we return the positive response. */ -@@ -2507,7 +2810,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - *ke = key_entry_create_rrset(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, - NULL, *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } else if(subtype == VAL_CLASS_NODATA || - subtype == VAL_CLASS_NAMEERROR) { - /* NODATA means that the qname exists, but that there was -@@ -2539,12 +2842,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - case sec_status_insecure: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral proved not a delegation point"); - *ke = NULL; -- return 1; -+ return 0; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral did not prove no DS."); -@@ -2556,10 +2859,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - break; - } - -+ if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ log_err("malloc failure in ds_response_to_ke for " -+ "NSEC3 cache"); -+ reason = "malloc failure"; -+ errinf_ede(qstate, reason, 0); -+ goto return_bogus; -+ } - sec = nsec3_prove_nods(qstate->env, ve, - msg->rep->rrsets + msg->rep->an_numrrsets, - msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, -- &reason_bogus, qstate); -+ &reason_bogus, qstate, &vq->nsec3_cache_table); - switch(sec) { - case sec_status_insecure: - /* case insecure also continues to unsigned -@@ -2572,18 +2882,19 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - case sec_status_indeterminate: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral proved no delegation"); - *ke = NULL; -- return 1; -+ return 0; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral did not prove no DS."); - errinf_ede(qstate, reason, reason_bogus); - goto return_bogus; - case sec_status_unchecked: -+ return 2; - default: - /* NSEC3 proof did not work */ - break; -@@ -2620,13 +2931,13 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - goto return_bogus; - } - sec = val_verify_rrset_entry(qstate->env, ve, cname, -- vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate); -+ vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate, &verified); - if(sec == sec_status_secure) { - verbose(VERB_ALGO, "CNAME validated, " - "proof that DS does not exist"); - /* and that it is not a referral point */ - *ke = NULL; -- return 1; -+ return 0; - } - errinf(qstate, "CNAME in DS response was not secure."); - errinf(qstate, reason); -@@ -2649,7 +2960,7 @@ return_bogus: - *ke = key_entry_create_bad(qstate->region, qinfo->qname, - qinfo->qname_len, qinfo->qclass, - BOGUS_KEY_TTL, *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } - - /** -@@ -2670,17 +2981,31 @@ return_bogus: - static void - process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, -- struct sock_list* origin) -+ struct sock_list* origin, int* suspend) - { - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - struct key_entry_key* dske = NULL; - uint8_t* olds = vq->empty_DS_name; -+ int ret; -+ *suspend = 0; - vq->empty_DS_name = NULL; -- if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { -+ ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske); -+ if(ret != 0) { -+ switch(ret) { -+ case 1: - log_err("malloc failure in process_ds_response"); - vq->key_entry = NULL; /* make it error */ - vq->state = VAL_VALIDATE_STATE; - return; -+ case 2: -+ *suspend = 1; -+ return; -+ default: -+ log_err("unhandled error value for ds_response_to_ke"); -+ vq->key_entry = NULL; /* make it error */ -+ vq->state = VAL_VALIDATE_STATE; -+ return; -+ } - } - if(dske == NULL) { - vq->empty_DS_name = regional_alloc_init(qstate->region, -@@ -2927,9 +3252,26 @@ val_inform_super(struct module_qstate* qstate, int id, - return; - } - if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { -+ int suspend; - process_ds_response(super, vq, id, qstate->return_rcode, -- qstate->return_msg, &qstate->qinfo, -- qstate->reply_origin); -+ qstate->return_msg, &qstate->qinfo, -+ qstate->reply_origin, &suspend); -+ /* If NSEC3 was needed during validation, NULL the NSEC3 cache; -+ * it will be re-initiated if needed later on. -+ * Validation (and the cache table) are happening/allocated in -+ * the super qstate whilst the RRs are allocated (and pointed -+ * to) in this sub qstate. */ -+ if(vq->nsec3_cache_table.ct) { -+ vq->nsec3_cache_table.ct = NULL; -+ } -+ if(suspend) { -+ /* deep copy the return_msg to vq->sub_ds_msg; it will -+ * be resumed later in the super state with the caveat -+ * that the initial calculations will be re-caclulated -+ * and re-suspended there before continuing. */ -+ vq->sub_ds_msg = dns_msg_deepcopy_region( -+ qstate->return_msg, super->region); -+ } - return; - } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { - process_dnskey_response(super, vq, id, qstate->return_rcode, -@@ -2943,8 +3285,15 @@ val_inform_super(struct module_qstate* qstate, int id, - void - val_clear(struct module_qstate* qstate, int id) - { -+ struct val_qstate* vq; - if(!qstate) - return; -+ vq = (struct val_qstate*)qstate->minfo[id]; -+ if(vq) { -+ if(vq->suspend_timer) { -+ comm_timer_delete(vq->suspend_timer); -+ } -+ } - /* everything is allocated in the region, so assign NULL */ - qstate->minfo[id] = NULL; - } -diff --git a/unbound-1.16.2/validator/validator.h b/unbound-1.16.2/validator/validator.h -index 694e4c8..72f44b1 100644 ---- a/unbound-1.16.2/validator/validator.h -+++ b/unbound-1.16.2/validator/validator.h -@@ -45,11 +45,13 @@ - #include "util/module.h" - #include "util/data/msgreply.h" - #include "validator/val_utils.h" -+#include "validator/val_nsec3.h" - struct val_anchors; - struct key_cache; - struct key_entry_key; - struct val_neg_cache; - struct config_strlist; -+struct comm_timer; - - /** - * This is the TTL to use when a trust anchor fails to prime. A trust anchor -@@ -215,6 +217,19 @@ struct val_qstate { - - /** true if this state is waiting to prime a trust anchor */ - int wait_prime_ta; -+ -+ /** State to continue with RRSIG validation in a message later */ -+ int msg_signatures_state; -+ /** The rrset index for the msg signatures to continue from */ -+ size_t msg_signatures_index; -+ /** Cache table for NSEC3 hashes */ -+ struct nsec3_cache_table nsec3_cache_table; -+ /** DS message from sub if it got suspended from NSEC3 calculations */ -+ struct dns_msg* sub_ds_msg; -+ /** The timer to resume processing msg signatures */ -+ struct comm_timer* suspend_timer; -+ /** Number of suspends */ -+ int suspend_count; - }; - - /** -@@ -262,4 +277,7 @@ void val_clear(struct module_qstate* qstate, int id); - */ - size_t val_get_mem(struct module_env* env, int id); - -+/** Timer callback for msg signatures continue timer */ -+void validate_suspend_timer_cb(void* arg); -+ - #endif /* VALIDATOR_VALIDATOR_H */ diff --git a/SOURCES/unbound-1.16-control-key-perms.patch b/SOURCES/unbound-1.16-control-key-perms.patch deleted file mode 100644 index de3597c..0000000 --- a/SOURCES/unbound-1.16-control-key-perms.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -index 4a358f6bd..c2a79a242 100644 ---- a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -+++ b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -@@ -204,7 +204,8 @@ fi - # remove unused permissions - chmod o-rw \ - "$SVR_BASE.pem" \ -- "$SVR_BASE.key" \ -+ "$SVR_BASE.key" -+chmod g+r,o-rw \ - "$CTL_BASE.pem" \ - "$CTL_BASE.key" - diff --git a/SOURCES/unbound-1.16-control-t-flag.patch b/SOURCES/unbound-1.16-control-t-flag.patch deleted file mode 100644 index 581db24..0000000 --- a/SOURCES/unbound-1.16-control-t-flag.patch +++ /dev/null @@ -1,129 +0,0 @@ -commit 6d1e61173bbf44dae458c361be63217f7e9e5599 -Author: W.C.A. Wijngaards -Date: Thu Mar 28 09:58:03 2024 +0100 - - - Fix #1034: DoT forward-zone via unbound-control. - -diff --git a/unbound-1.16.2/daemon/remote.c b/unbound-1.16.2/daemon/remote.c -index 5d79eafd..cbce1198 100644 ---- a/unbound-1.16.2/daemon/remote.c -+++ b/unbound-1.16.2/daemon/remote.c -@@ -2097,7 +2097,7 @@ do_forward(RES* ssl, struct worker* worker, char* args) - - static int - parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp, -- int* insecure, int* prime) -+ int* insecure, int* prime, int* tls) - { - char* zonename; - char* rest; -@@ -2112,6 +2112,8 @@ parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp, - *insecure = 1; - else if(*args == 'p' && prime) - *prime = 1; -+ else if(*args == 't' && tls) -+ *tls = 1; - else { - (void)ssl_printf(ssl, "error: unknown option %s\n", args); - return 0; -@@ -2144,11 +2146,13 @@ static void - do_forward_add(RES* ssl, struct worker* worker, char* args) - { - struct iter_forwards* fwd = worker->env.fwds; -- int insecure = 0; -+ int insecure = 0, tls = 0; - uint8_t* nm = NULL; - struct delegpt* dp = NULL; -- if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL)) -+ if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL, &tls)) - return; -+ if(tls) -+ dp->ssl_upstream = 1; - if(insecure && worker->env.anchors) { - if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, - nm)) { -@@ -2174,7 +2178,7 @@ do_forward_remove(RES* ssl, struct worker* worker, char* args) - struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0; - uint8_t* nm = NULL; -- if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) -+ if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) - return; - if(insecure && worker->env.anchors) - anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, -@@ -2189,11 +2193,13 @@ static void - do_stub_add(RES* ssl, struct worker* worker, char* args) - { - struct iter_forwards* fwd = worker->env.fwds; -- int insecure = 0, prime = 0; -+ int insecure = 0, prime = 0, tls = 0; - uint8_t* nm = NULL; - struct delegpt* dp = NULL; -- if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime)) -+ if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime, &tls)) - return; -+ if(tls) -+ dp->ssl_upstream = 1; - if(insecure && worker->env.anchors) { - if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, - nm)) { -@@ -2232,7 +2238,7 @@ do_stub_remove(RES* ssl, struct worker* worker, char* args) - struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0; - uint8_t* nm = NULL; -- if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) -+ if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) - return; - if(insecure && worker->env.anchors) - anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, -diff --git a/unbound-1.16.2/doc/unbound-control.8.in b/unbound-1.16.2/doc/unbound-control.8.in -index 7823de3a..642b4c94 100644 ---- a/unbound-1.16.2/doc/unbound-control.8.in -+++ b/unbound-1.16.2/doc/unbound-control.8.in -@@ -239,22 +239,24 @@ still be bogus, use \fBflush_zone\fR to remove it), does not affect the config f - .B insecure_remove \fIzone - Removes domain\-insecure for the given zone. - .TP --.B forward_add \fR[\fI+i\fR] \fIzone addr ... -+.B forward_add \fR[\fI+it\fR] \fIzone addr ... - Add a new forward zone to running Unbound. With +i option also adds a - \fIdomain\-insecure\fR for the zone (so it can resolve insecurely if you have - a DNSSEC root trust anchor configured for other names). - The addr can be IP4, IP6 or nameserver names, like \fIforward-zone\fR config - in unbound.conf. -+The +t option sets it to use tls upstream, like \fIforward\-tls\-upstream\fR: yes. - .TP - .B forward_remove \fR[\fI+i\fR] \fIzone - Remove a forward zone from running Unbound. The +i also removes a - \fIdomain\-insecure\fR for the zone. - .TP --.B stub_add \fR[\fI+ip\fR] \fIzone addr ... -+.B stub_add \fR[\fI+ipt\fR] \fIzone addr ... - Add a new stub zone to running Unbound. With +i option also adds a - \fIdomain\-insecure\fR for the zone. With +p the stub zone is set to prime, - without it it is set to notprime. The addr can be IP4, IP6 or nameserver - names, like the \fIstub-zone\fR config in unbound.conf. -+The +t option sets it to use tls upstream, like \fIstub\-tls\-upstream\fR: yes. - .TP - .B stub_remove \fR[\fI+i\fR] \fIzone - Remove a stub zone from running Unbound. The +i also removes a -diff --git a/unbound-1.16.2/smallapp/unbound-control.c b/unbound-1.16.2/smallapp/unbound-control.c -index c4f73006..57b0787d 100644 ---- a/unbound-1.16.2/smallapp/unbound-control.c -+++ b/unbound-1.16.2/smallapp/unbound-control.c -@@ -150,12 +150,13 @@ usage(void) - printf(" list_local_data list local-data RRs in use\n"); - printf(" insecure_add zone add domain-insecure zone\n"); - printf(" insecure_remove zone remove domain-insecure zone\n"); -- printf(" forward_add [+i] zone addr.. add forward-zone with servers\n"); -+ printf(" forward_add [+it] zone addr.. add forward-zone with servers\n"); - printf(" forward_remove [+i] zone remove forward zone\n"); -- printf(" stub_add [+ip] zone addr.. add stub-zone with servers\n"); -+ printf(" stub_add [+ipt] zone addr.. add stub-zone with servers\n"); - printf(" stub_remove [+i] zone remove stub zone\n"); - printf(" +i also do dnssec insecure point\n"); - printf(" +p set stub to use priming\n"); -+ printf(" +t set to use tls upstream\n"); - printf(" forward [off | addr ...] without arg show forward setup\n"); - printf(" or off to turn off root forwarding\n"); - printf(" or give list of ip addresses\n"); diff --git a/SOURCES/unbound-1.16.2.tar.gz.asc b/SOURCES/unbound-1.16.2.tar.gz.asc deleted file mode 100644 index 0f94fb9..0000000 --- a/SOURCES/unbound-1.16.2.tar.gz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEE7fqj8spObrBWga+On28cLX4EX40FAmLnudYACgkQn28cLX4E -X43GmRAAoROXbktLR2AXGEECgPCFlHag9oNZosa3J5yR2vaV4e8eA6AMzPyZbl7P -LnLon8PZZR+pTW+dDRqakvzJIwXkLeONFgEdvd0cAghWAtPrKCDZIkCyeQj0OOv3 -wt1pRRl2PXUKNZZf0bzpTUIhVsHF/w5f5T/mFAZm49rUDboj77xgokmaFK4kei0I -Gz4W8Vx3TIwwJc8nea8GtCYIg3UKmR/TMznMFExAoKdMllzKuJnGx5lR/eU0+NRc -uwWEQhNJrHXZyWethp9swLCrOmDHcgBJOd04TqcDwSIZrw9VuT3/Uza3Tw73N7kr -PZvF2xSOASL+i91QP6tnkmQD5pAORVpUFN3NePEWV5922iG/pVipaYBbEyV3dfph -Y4QGwj8G6ppcfjV7gmlxsAOM2gnhD3rDqFmkxau6zB1kktHnV2aqlzIQo396ZBJQ -hKyIAJlNvpTiFaACD7/cFkE80awJnCD/qvXATN//BWHKytgO8eYg7fZGrxjbpIQk -XV/vVlOJWRXPyPBnp8MQyCIDe2eq2ELlMfYw62/TNDuj2qKsM/W03cem3GlveOa6 -tw8RVfFFjwZlCLbXSbmsKo+mWJ3jCAvb3/gql52vJDE5FuRz7MvptIVU6DVE1O+J -mQ3AoQ2Mq9iHsZePfze4sq531DMlWTgBMwqfBTWqMaTC/8VH5rg= -=Ax9n ------END PGP SIGNATURE----- diff --git a/SOURCES/unbound-1.21-CVE-2024-8508.patch b/SOURCES/unbound-1.21-CVE-2024-8508.patch deleted file mode 100644 index 36b3cdd..0000000 --- a/SOURCES/unbound-1.21-CVE-2024-8508.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 34de24d58bb5aa6fe3551512fc17cac08f65d93e Mon Sep 17 00:00:00 2001 -From: Yorgos Thessalonikefs -Date: Thu, 3 Oct 2024 14:46:57 +0200 -Subject: [PATCH] - Fix CVE-2024-8508, unbounded name compression could lead to - denial of service. - ---- - unbound-1.16.2/util/data/msgencode.c | 77 +++++++++++++++++----------- - 1 file changed, 46 insertions(+), 31 deletions(-) - -diff --git a/unbound-1.16.2/util/data/msgencode.c b/unbound-1.16.2/util/data/msgencode.c -index fe21cfb..f9e95e6 100644 ---- a/unbound-1.16.2/util/data/msgencode.c -+++ b/unbound-1.16.2/util/data/msgencode.c -@@ -62,6 +62,10 @@ - #define RETVAL_TRUNC -4 - /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */ - #define RETVAL_OK 0 -+/** Max compressions we are willing to perform; more than that will result -+ * in semi-compressed messages, or truncated even on TCP for huge messages, to -+ * avoid locking the CPU for long */ -+#define MAX_COMPRESSION_PER_MESSAGE 120 - - /** - * Data structure to help domain name compression in outgoing messages. -@@ -284,15 +288,17 @@ write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs, - - /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ - static int --compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, -- struct regional* region, struct compress_tree_node** tree, -- size_t owner_pos, uint16_t* owner_ptr, int owner_labs) -+compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, -+ struct regional* region, struct compress_tree_node** tree, -+ size_t owner_pos, uint16_t* owner_ptr, int owner_labs, -+ size_t* compress_count) - { - struct compress_tree_node* p; - struct compress_tree_node** insertpt = NULL; - if(!*owner_ptr) { - /* compress first time dname */ -- if((p = compress_tree_lookup(tree, key->rk.dname, -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ (p = compress_tree_lookup(tree, key->rk.dname, - owner_labs, &insertpt))) { - if(p->labs == owner_labs) - /* avoid ptr chains, since some software is -@@ -301,6 +307,7 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - if(!write_compressed_dname(pkt, key->rk.dname, - owner_labs, p)) - return RETVAL_TRUNC; -+ (*compress_count)++; - /* check if typeclass+4 ttl + rdatalen is available */ - if(sldns_buffer_remaining(pkt) < 4+4+2) - return RETVAL_TRUNC; -@@ -313,7 +320,8 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - if(owner_pos <= PTR_MAX_OFFSET) - *owner_ptr = htons(PTR_CREATE(owner_pos)); - } -- if(!compress_tree_store(key->rk.dname, owner_labs, -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ !compress_tree_store(key->rk.dname, owner_labs, - owner_pos, region, p, insertpt)) - return RETVAL_OUTMEM; - } else { -@@ -333,20 +341,24 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - - /** compress any domain name to the packet, return RETVAL_* */ - static int --compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, -- struct regional* region, struct compress_tree_node** tree) -+compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, -+ struct regional* region, struct compress_tree_node** tree, -+ size_t* compress_count) - { - struct compress_tree_node* p; - struct compress_tree_node** insertpt = NULL; - size_t pos = sldns_buffer_position(pkt); -- if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) { -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ (p = compress_tree_lookup(tree, dname, labs, &insertpt))) { - if(!write_compressed_dname(pkt, dname, labs, p)) - return RETVAL_TRUNC; -+ (*compress_count)++; - } else { - if(!dname_buffer_write(pkt, dname)) - return RETVAL_TRUNC; - } -- if(!compress_tree_store(dname, labs, pos, region, p, insertpt)) -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ !compress_tree_store(dname, labs, pos, region, p, insertpt)) - return RETVAL_OUTMEM; - return RETVAL_OK; - } -@@ -364,9 +376,9 @@ type_rdata_compressable(struct ub_packed_rrset_key* key) - - /** compress domain names in rdata, return RETVAL_* */ - static int --compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, -- struct regional* region, struct compress_tree_node** tree, -- const sldns_rr_descriptor* desc) -+compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, -+ struct regional* region, struct compress_tree_node** tree, -+ const sldns_rr_descriptor* desc, size_t* compress_count) - { - int labs, r, rdf = 0; - size_t dname_len, len, pos = sldns_buffer_position(pkt); -@@ -380,8 +392,8 @@ compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, - switch(desc->_wireformat[rdf]) { - case LDNS_RDF_TYPE_DNAME: - labs = dname_count_size_labels(rdata, &dname_len); -- if((r=compress_any_dname(rdata, pkt, labs, region, -- tree)) != RETVAL_OK) -+ if((r=compress_any_dname(rdata, pkt, labs, region, -+ tree, compress_count)) != RETVAL_OK) - return r; - rdata += dname_len; - todolen -= dname_len; -@@ -449,7 +461,8 @@ static int - packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - uint16_t* num_rrs, time_t timenow, struct regional* region, - int do_data, int do_sig, struct compress_tree_node** tree, -- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) -+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, -+ size_t* compress_count) - { - size_t i, j, owner_pos; - int r, owner_labs; -@@ -477,9 +490,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - for(i=0; icount; i++) { - /* rrset roundrobin */ - j = (i + rr_offset) % data->count; -- if((r=compress_owner(key, pkt, region, tree, -- owner_pos, &owner_ptr, owner_labs)) -- != RETVAL_OK) -+ if((r=compress_owner(key, pkt, region, tree, -+ owner_pos, &owner_ptr, owner_labs, -+ compress_count)) != RETVAL_OK) - return r; - sldns_buffer_write(pkt, &key->rk.type, 2); - sldns_buffer_write(pkt, &key->rk.rrset_class, 2); -@@ -489,8 +502,8 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust); - if(c) { - if((r=compress_rdata(pkt, data->rr_data[j], -- data->rr_len[j], region, tree, c)) -- != RETVAL_OK) -+ data->rr_len[j], region, tree, c, -+ compress_count)) != RETVAL_OK) - return r; - } else { - if(sldns_buffer_remaining(pkt) < data->rr_len[j]) -@@ -510,9 +523,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - return RETVAL_TRUNC; - sldns_buffer_write(pkt, &owner_ptr, 2); - } else { -- if((r=compress_any_dname(key->rk.dname, -- pkt, owner_labs, region, tree)) -- != RETVAL_OK) -+ if((r=compress_any_dname(key->rk.dname, -+ pkt, owner_labs, region, tree, -+ compress_count)) != RETVAL_OK) - return r; - if(sldns_buffer_remaining(pkt) < - 4+4+data->rr_len[i]) -@@ -544,7 +557,8 @@ static int - insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - sldns_buffer* pkt, size_t rrsets_before, time_t timenow, - struct regional* region, struct compress_tree_node** tree, -- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) -+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, -+ size_t* compress_count) - { - int r; - size_t i, setstart; -@@ -560,7 +574,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 1, 1, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - /* Bad, but if due to size must set TC bit */ - /* trim off the rrset neatly. */ -@@ -573,7 +587,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 1, 0, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - sldns_buffer_set_position(pkt, setstart); - return r; -@@ -584,7 +598,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 0, 1, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - sldns_buffer_set_position(pkt, setstart); - return r; -@@ -677,6 +691,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - struct compress_tree_node* tree = 0; - int r; - size_t rr_offset; -+ size_t compress_count=0; - - sldns_buffer_clear(buffer); - if(udpsize < sldns_buffer_limit(buffer)) -@@ -723,7 +738,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - arep.rrsets = &qinfo->local_alias->rrset; - if((r=insert_section(&arep, 1, &ancount, buffer, 0, - timezero, region, &tree, LDNS_SECTION_ANSWER, -- qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { -+ qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 6, ancount); -@@ -738,7 +753,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - /* insert answer section */ - if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, - 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 6, ancount); -@@ -756,7 +771,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, - rep->an_numrrsets, timenow, region, &tree, - LDNS_SECTION_AUTHORITY, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 8, nscount); -@@ -773,7 +788,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, - rep->an_numrrsets + rep->ns_numrrsets, timenow, region, - &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* no need to set TC bit, this is the additional */ - sldns_buffer_write_u16_at(buffer, 10, arcount); --- -2.47.0 - diff --git a/SOURCES/unbound-1.23.1-CVE-2025-5994.patch b/SOURCES/unbound-1.23.1-CVE-2025-5994.patch deleted file mode 100644 index a6e034d..0000000 --- a/SOURCES/unbound-1.23.1-CVE-2025-5994.patch +++ /dev/null @@ -1,2255 +0,0 @@ -diff --git a/unbound-1.16.2/edns-subnet/addrtree.c b/unbound-1.16.2/edns-subnet/addrtree.c -index 180a022..ebe71b9 100644 ---- a/unbound-1.16.2/edns-subnet/addrtree.c -+++ b/unbound-1.16.2/edns-subnet/addrtree.c -@@ -97,6 +97,7 @@ node_create(struct addrtree *tree, void *elem, addrlen_t scope, - tree->node_count++; - node->scope = scope; - node->ttl = ttl; -+ node->only_match_scope_zero = 0; - node->edge[0] = NULL; - node->edge[1] = NULL; - node->parent_edge = NULL; -@@ -155,6 +156,7 @@ clean_node(struct addrtree *tree, struct addrnode *node) - if (!node->elem) return; - tree->size_bytes -= tree->sizefunc(node->elem); - tree->delfunc(tree->env, node->elem); -+ node->only_match_scope_zero = 0; - node->elem = NULL; - } - -@@ -358,7 +360,7 @@ issub(const addrkey_t *s1, addrlen_t l1, - void - addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, -- time_t now) -+ time_t now, int only_match_scope_zero) - { - struct addrnode *newnode, *node; - struct addredge *edge; -@@ -381,6 +383,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - /* update this node's scope and data */ - clean_node(tree, node); - node->ttl = ttl; -+ node->only_match_scope_zero = only_match_scope_zero; - node->elem = elem; - node->scope = scope; - tree->size_bytes += tree->sizefunc(elem); -@@ -447,6 +450,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - newnode->elem = elem; - newnode->scope = scope; - newnode->ttl = ttl; -+ newnode->only_match_scope_zero = only_match_scope_zero; - } - - tree->size_bytes += node_size(tree, newnode); -@@ -483,7 +487,8 @@ addrtree_find(struct addrtree *tree, const addrkey_t *addr, - /* Current node more specific then question. */ - log_assert(depth <= sourcemask); - /* does this node have data? if yes, see if we have a match */ -- if (node->elem && node->ttl >= now) { -+ if (node->elem && node->ttl >= now && -+ !(sourcemask != 0 && node->only_match_scope_zero)) { - /* saved at wrong depth */; - log_assert(node->scope >= depth); - if (depth == node->scope || -diff --git a/unbound-1.16.2/edns-subnet/addrtree.h b/unbound-1.16.2/edns-subnet/addrtree.h -index 1aea54e..0bc1837 100644 ---- a/unbound-1.16.2/edns-subnet/addrtree.h -+++ b/unbound-1.16.2/edns-subnet/addrtree.h -@@ -95,6 +95,10 @@ struct addrnode { - time_t ttl; - /** Number of significant bits in address. */ - addrlen_t scope; -+ /** Only use the element for queries for subnet/0. Set if the query -+ * for /0 was answered with scope 0. For query /x answer scope 0, -+ * they can match anything and this is false. */ -+ int only_match_scope_zero; - /** A node can have 0-2 edges, set to NULL for unused */ - struct addredge *edge[2]; - /** edge between this node and parent */ -@@ -157,11 +161,12 @@ void addrtree_delete(struct addrtree *tree); - * @param scope: Number of significant bits in addr. - * @param elem: data to store in the tree. - * @param ttl: elem is valid up to this time, seconds. -+ * @param only_match_scope_zero: set for when query /0 has scope /0 answer. - * @param now: Current time in seconds. - */ - void addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, -- time_t now); -+ time_t now, int only_match_scope_zero); - - /** - * Find a node containing an element in the tree. -diff --git a/unbound-1.16.2/edns-subnet/subnetmod.c b/unbound-1.16.2/edns-subnet/subnetmod.c -index 7544611..53d3048 100644 ---- a/unbound-1.16.2/edns-subnet/subnetmod.c -+++ b/unbound-1.16.2/edns-subnet/subnetmod.c -@@ -51,10 +51,12 @@ - #include "services/cache/dns.h" - #include "util/module.h" - #include "util/regional.h" -+#include "util/fptr_wlist.h" - #include "util/storage/slabhash.h" - #include "util/config_file.h" - #include "util/data/msgreply.h" - #include "sldns/sbuffer.h" -+#include "sldns/wire2str.h" - #include "iterator/iter_utils.h" - - /** externally called */ -@@ -151,10 +153,12 @@ int ecs_whitelist_check(struct query_info* qinfo, - - /* Cache by default, might be disabled after parsing EDNS option - * received from nameserver. */ -- if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) { -+ if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL) -+ && sq->ecs_client_in.subnet_validdata) { - qstate->no_cache_store = 0; - } - -+ sq->subnet_sent_no_subnet = 0; - if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && - qstate->env->cfg->client_subnet_always_forward) || - ecs_is_whitelisted(sn_env->whitelist, -@@ -165,6 +169,14 @@ int ecs_whitelist_check(struct query_info* qinfo, - * set. */ - if(!edns_opt_list_find(qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode)) { -+ /* if the client is not wanting an EDNS subnet option, -+ * omit it and store that we omitted it but actually -+ * are doing EDNS subnet to the server. */ -+ if(sq->ecs_server_out.subnet_source_mask == 0) { -+ sq->subnet_sent_no_subnet = 1; -+ sq->subnet_sent = 0; -+ return 1; -+ } - subnet_ecs_opt_list_append(&sq->ecs_server_out, - &qstate->edns_opts_back_out, qstate, region); - } -@@ -331,6 +343,7 @@ update_cache(struct module_qstate *qstate, int id) - struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; - struct ecs_data *edns = &sq->ecs_client_in; - size_t i; -+ int only_match_scope_zero; - - /* We already calculated hash upon lookup (lookup_and_reply) if we were - * allowed to look in the ECS cache */ -@@ -392,9 +405,12 @@ update_cache(struct module_qstate *qstate, int id) - reply_info_set_ttls(rep, *qstate->env->now); - rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */ - rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ -+ if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0) -+ only_match_scope_zero = 1; -+ else only_match_scope_zero = 0; - addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, - edns->subnet_source_mask, sq->max_scope, rep, -- rep->ttl, *qstate->env->now); -+ rep->ttl, *qstate->env->now, only_match_scope_zero); - - lock_rw_unlock(&lru_entry->lock); - if (need_to_insert) { -@@ -475,6 +491,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) - return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); - } - -+/** -+ * Create sub request that looks up the query. -+ * @param qstate: query state -+ * @param sq: subnet qstate -+ * @return false on failure. -+ */ -+static int -+generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) -+{ -+ struct module_qstate* subq = NULL; -+ uint16_t qflags = 0; /* OPCODE QUERY, no flags */ -+ int prime = 0; -+ int valrec = 0; -+ struct query_info qinf; -+ qinf.qname = qstate->qinfo.qname; -+ qinf.qname_len = qstate->qinfo.qname_len; -+ qinf.qtype = qstate->qinfo.qtype; -+ qinf.qclass = qstate->qinfo.qclass; -+ qinf.local_alias = NULL; -+ -+ qflags |= BIT_RD; -+ if((qstate->query_flags & BIT_CD)!=0) { -+ qflags |= BIT_CD; -+ valrec = 1; -+ } -+ -+ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); -+ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, -+ &subq)) { -+ return 0; -+ } -+ if(subq) { -+ /* It is possible to access the subquery module state. */ -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ subq->no_cache_store = 1; -+ } -+ } -+ return 1; -+} -+ -+/** -+ * Perform the query without subnet -+ * @param qstate: query state -+ * @param sq: subnet qstate -+ * @return module state -+ */ -+static enum module_ext_state -+generate_lookup_without_subnet(struct module_qstate *qstate, -+ struct subnet_qstate* sq) -+{ -+ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); -+ if(!generate_sub_request(qstate, sq)) { -+ verbose(VERB_ALGO, "Could not generate sub query"); -+ qstate->return_rcode = LDNS_RCODE_FORMERR; -+ qstate->return_msg = NULL; -+ return module_finished; -+ } -+ sq->wait_subquery = 1; -+ return module_wait_subquery; -+} -+ - static enum module_ext_state - eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - { -@@ -495,7 +574,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - } - - /* We have not asked for subnet data */ -- if (!sq->subnet_sent) { -+ if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { - if (s_in->subnet_validdata) - verbose(VERB_QUERY, "subnetcache: received spurious data"); - if (sq->subnet_downstream) /* Copy back to client */ -@@ -504,22 +583,27 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - } - - /* subnet sent but nothing came back */ -- if (!s_in->subnet_validdata) { -+ if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { - /* The authority indicated no support for edns subnet. As a - * consequence the answer ended up in the regular cache. It - * is still useful to put it in the edns subnet cache for - * when a client explicitly asks for subnet specific answer. */ - verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); -- if(!sq->started_no_cache_store) { -- lock_rw_wrlock(&sne->biglock); -- update_cache(qstate, id); -- lock_rw_unlock(&sne->biglock); -- } -- if (sq->subnet_downstream) -- cp_edns_bad_response(c_out, c_in); -- return module_finished; -+ return generate_lookup_without_subnet(qstate, sq); - } -- -+ -+ /* Purposefully there was no sent subnet, and there is consequently -+ * no subnet in the answer. If there was, use the subnet in the answer -+ * anyway. But if there is not, treat it as a prefix 0 answer. */ -+ if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { -+ /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ -+ s_in->subnet_addr_fam = s_out->subnet_addr_fam; -+ s_in->subnet_source_mask = 0; -+ s_in->subnet_scope_mask = 0; -+ memset(s_in->subnet_addr, 0, INET6_SIZE); -+ s_in->subnet_validdata = 1; -+ } -+ - /* Being here means we have asked for and got a subnet specific - * answer. Also, the answer from the authority is not yet cached - * anywhere. */ -@@ -530,13 +614,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - !common_prefix(s_out->subnet_addr, s_in->subnet_addr, - s_out->subnet_source_mask)) - { -- /* we can not accept, restart query without option */ -+ /* we can not accept, perform query without option */ - verbose(VERB_QUERY, "subnetcache: forged data"); - s_out->subnet_validdata = 0; - (void)edns_opt_list_remove(&qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode); - sq->subnet_sent = 0; -- return module_restart_next; -+ sq->subnet_sent_no_subnet = 0; -+ return generate_lookup_without_subnet(qstate, sq); - } - - lock_rw_wrlock(&sne->biglock); -@@ -656,6 +741,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, - edns_opt_list_remove(&qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode); - sq->subnet_sent = 0; -+ sq->subnet_sent_no_subnet = 0; - memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); - } else if (!sq->track_max_scope && - FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && -@@ -674,6 +760,24 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, - return 1; - } - -+/** verbose print edns subnet option in pretty print */ -+static void -+subnet_log_print(const char* s, struct edns_option* ecs_opt) -+{ -+ if(verbosity >= VERB_ALGO) { -+ char buf[256]; -+ char* str = buf; -+ size_t str_len = sizeof(buf); -+ if(!ecs_opt) { -+ verbose(VERB_ALGO, "%s (null)", s); -+ return; -+ } -+ (void)sldns_wire2str_edns_subnet_print(&str, &str_len, -+ ecs_opt->opt_data, ecs_opt->opt_len); -+ verbose(VERB_ALGO, "%s %s", s, buf); -+ } -+} -+ - int - ecs_edns_back_parsed(struct module_qstate* qstate, int id, - void* ATTR_UNUSED(cbargs)) -@@ -688,6 +792,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, - qstate->env->cfg->client_subnet_opcode)) && - parse_subnet_option(ecs_opt, &sq->ecs_server_in) && - sq->subnet_sent && sq->ecs_server_in.subnet_validdata) { -+ subnet_log_print("answer has edns subnet", ecs_opt); - /* Only skip global cache store if we sent an ECS option - * and received one back. Answers from non-whitelisted - * servers will end up in global cache. Answers for -@@ -698,6 +803,12 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, - sq->ecs_server_in.subnet_scope_mask > - sq->max_scope)) - sq->max_scope = sq->ecs_server_in.subnet_scope_mask; -+ } else if(sq->subnet_sent_no_subnet) { -+ /* The answer can be stored as scope 0, not in global cache. */ -+ qstate->no_cache_store = 1; -+ } else if(sq->subnet_sent) { -+ /* Need another query to be able to store in global cache. */ -+ qstate->no_cache_store = 1; - } - - return 1; -@@ -715,6 +826,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - strmodulevent(event)); - log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); - -+ if(sq && sq->wait_subquery_done) { -+ /* The subquery lookup returned. */ -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ if(!sq->started_no_cache_store && -+ qstate->return_msg) { -+ lock_rw_wrlock(&sne->biglock); -+ update_cache(qstate, id); -+ lock_rw_unlock(&sne->biglock); -+ } -+ if (sq->subnet_downstream) -+ cp_edns_bad_response(&sq->ecs_client_out, -+ &sq->ecs_client_in); -+ /* It is a scope zero lookup, append edns subnet -+ * option to the querier. */ -+ subnet_ecs_opt_list_append(&sq->ecs_client_out, -+ &qstate->edns_opts_front_out, qstate, -+ qstate->region); -+ } -+ sq->wait_subquery_done = 0; -+ qstate->ext_state[id] = module_finished; -+ qstate->no_cache_store = sq->started_no_cache_store; -+ qstate->no_cache_lookup = sq->started_no_cache_lookup; -+ return; -+ } - if((event == module_event_new || event == module_event_pass) && - sq == NULL) { - struct edns_option* ecs_opt; -@@ -725,6 +862,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - } - - sq = (struct subnet_qstate*)qstate->minfo[id]; -+ if(sq->wait_subquery) -+ return; /* Wait for that subquery to return */ - - if((ecs_opt = edns_opt_list_find( - qstate->edns_opts_front_in, -@@ -736,6 +875,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - qstate->ext_state[id] = module_finished; - return; - } -+ subnet_log_print("query has edns subnet", ecs_opt); - sq->subnet_downstream = 1; - } - else if(qstate->mesh_info->reply_list) { -@@ -748,6 +888,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - /* No clients are interested in result or we could not - * parse it, we don't do client subnet */ - sq->ecs_server_out.subnet_validdata = 0; -+ if(edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ /* aggregated this deaggregated state */ -+ qstate->ext_state[id] = -+ generate_lookup_without_subnet( -+ qstate, sq); -+ return; -+ } - verbose(VERB_ALGO, "subnetcache: pass to next module"); - qstate->ext_state[id] = module_wait_module; - return; -@@ -775,10 +923,25 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - subnet_ecs_opt_list_append(&sq->ecs_client_out, - &qstate->edns_opts_front_out, qstate, - qstate->region); -+ if(verbosity >= VERB_ALGO) { -+ subnet_log_print("reply has edns subnet", -+ edns_opt_list_find( -+ qstate->edns_opts_front_out, -+ qstate->env->cfg-> -+ client_subnet_opcode)); -+ } - return; - } - lock_rw_unlock(&sne->biglock); - } -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ /* client asked for resolution without edns subnet */ -+ qstate->ext_state[id] = generate_lookup_without_subnet( -+ qstate, sq); -+ return; -+ } - - sq->ecs_server_out.subnet_addr_fam = - sq->ecs_client_in.subnet_addr_fam; -@@ -815,6 +978,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - qstate->ext_state[id] = module_wait_module; - return; - } -+ if(sq && sq->wait_subquery) -+ return; /* Wait for that subquery to return */ - /* Query handed back by next module, we have a 'final' answer */ - if(sq && event == module_event_moddone) { - qstate->ext_state[id] = eval_response(qstate, id, sq); -@@ -823,6 +988,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - subnet_ecs_opt_list_append(&sq->ecs_client_out, - &qstate->edns_opts_front_out, qstate, - qstate->region); -+ if(verbosity >= VERB_ALGO) { -+ subnet_log_print("reply has edns subnet", -+ edns_opt_list_find( -+ qstate->edns_opts_front_out, -+ qstate->env->cfg-> -+ client_subnet_opcode)); -+ } - } - qstate->no_cache_store = sq->started_no_cache_store; - qstate->no_cache_lookup = sq->started_no_cache_lookup; -@@ -856,10 +1028,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), - } - - void --subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), -- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) -+subnetmod_inform_super(struct module_qstate *qstate, int id, -+ struct module_qstate *super) - { -- /* Not used */ -+ struct subnet_qstate* super_sq = -+ (struct subnet_qstate*)super->minfo[id]; -+ log_query_info(VERB_ALGO, "subnetcache inform_super: query", -+ &super->qinfo); -+ super_sq->wait_subquery = 0; -+ super_sq->wait_subquery_done = 1; -+ if(qstate->return_rcode != LDNS_RCODE_NOERROR || -+ !qstate->return_msg) { -+ super->return_msg = NULL; -+ super->return_rcode = LDNS_RCODE_SERVFAIL; -+ return; -+ } -+ super->return_rcode = LDNS_RCODE_NOERROR; -+ super->return_msg = dns_copy_msg(qstate->return_msg, super->region); -+ if(!super->return_msg) { -+ log_err("subnetcache: copy response, out of memory"); -+ super->return_rcode = LDNS_RCODE_SERVFAIL; -+ } - } - - size_t -diff --git a/unbound-1.16.2/edns-subnet/subnetmod.h b/unbound-1.16.2/edns-subnet/subnetmod.h -index f0bcaad..3893820 100644 ---- a/unbound-1.16.2/edns-subnet/subnetmod.h -+++ b/unbound-1.16.2/edns-subnet/subnetmod.h -@@ -85,6 +85,13 @@ struct subnet_qstate { - struct ecs_data ecs_server_out; - int subnet_downstream; - int subnet_sent; -+ /** -+ * If there was no subnet sent because the client used source prefix -+ * length 0 for omitting the information. Then the answer is cached -+ * like subnet was a /0 scope. Like the subnet_sent flag, but when -+ * the EDNS subnet option is omitted because the client asked. -+ */ -+ int subnet_sent_no_subnet; - /** keep track of longest received scope, set after receiving CNAME for - * incoming QNAME. */ - int track_max_scope; -@@ -95,6 +102,10 @@ struct subnet_qstate { - int started_no_cache_store; - /** has the subnet module been started with no_cache_lookup? */ - int started_no_cache_lookup; -+ /** Wait for subquery that has been started for nonsubnet lookup. */ -+ int wait_subquery; -+ /** The subquery waited for is done. */ -+ int wait_subquery_done; - }; - - void subnet_data_delete(void* d, void* ATTR_UNUSED(arg)); -diff --git a/unbound-1.16.2/iterator/iterator.c b/unbound-1.16.2/iterator/iterator.c -index da9b799..3cacd5d 100644 ---- a/unbound-1.16.2/iterator/iterator.c -+++ b/unbound-1.16.2/iterator/iterator.c -@@ -3982,10 +3982,10 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, - /* like packet got dropped */ - goto handle_it; - } -- if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { -- log_err("unable to call edns_back_parsed callback"); -- goto handle_it; -- } -+ } -+ if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { -+ log_err("unable to call edns_back_parsed callback"); -+ goto handle_it; - } - - /* remove CD-bit, we asked for in case we handle validation ourself */ -diff --git a/unbound-1.16.2/testcode/unitecs.c b/unbound-1.16.2/testcode/unitecs.c -index b240bfc..68d6907 100644 ---- a/unbound-1.16.2/testcode/unitecs.c -+++ b/unbound-1.16.2/testcode/unitecs.c -@@ -173,7 +173,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow); -+ addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow, 0); - /* This should always hold because no items ever expire. They - * could be overwritten, though. */ - unit_assert( count <= t->node_count ); -@@ -189,7 +189,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, i + 10, i); -+ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); - free(k); - unit_assert( !addrtree_inconsistent(t) ); - } -@@ -201,7 +201,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, i + 10, i); -+ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); - unit_assert( t->node_count <= 27); - free(k); - unit_assert( !addrtree_inconsistent(t) ); -diff --git a/unbound-1.16.2/testdata/subnet_noecs_mult.rpl b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl -new file mode 100644 -index 0000000..3e2acef ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl -@@ -0,0 +1,334 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet from server multiple times -+; Multiple queries are sent to a server that does not reply with the -+; edns-subnet option. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 50 52 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 00 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 02 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.3.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 03 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+; The answer for a query without subnet -+;ENTRY_BEGIN -+;MATCH opcode qtype qname -+;ADJUST copy_id -+;REPLY QR AA NOERROR -+;SECTION QUESTION -+;www.example.com. IN A -+;SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+;ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 53 57 -+ ADDRESS 1.2.3.4 -+; The answer for a query without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 10 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 00 00 ; 127.0.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+STEP 20 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 02 00 ; 127.2.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+STEP 30 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 03 00 ; 127.3.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+; recursion happens here. -+; The upstream server RANGE starts responding at STEP 50. -+STEP 50 TRAFFIC -+ -+; The upstream server now responds for the nonsubnet response. -+STEP 55 TRAFFIC -+ -+STEP 60 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.3.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 03 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.2.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 02 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 80 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.0.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 00 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_noecs_refused.rpl b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl -new file mode 100644 -index 0000000..39fbe85 ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl -@@ -0,0 +1,159 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet support but it is refused -+; The query is sent to a server that does not reply with the edns-subnet -+; option. The upstream server sends rcode refused. That results in a -+; NULL return_msg. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+; This matches the no EDNS subnet info queries that are made for the -+; fallback without subnet. The answer is refused. -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA REFUSED -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+ENTRY_END -+ -+; This matches the initial query with edns subnet in the query, -+; the answer has no edns subnet in the reply. -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 1 QUERY -+;ENTRY_BEGIN -+;REPLY RD DO -+;SECTION QUESTION -+;www.example.com. IN A -+; but send this query with subnet scope zero in the query, because that -+; makes the reply possibly get stored in the cache. -+; -+; query with subnet 0.0.0.0/0. -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; recursion happens here. -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO SERVFAIL -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_noecs_support.rpl b/unbound-1.16.2/testdata/subnet_noecs_support.rpl -new file mode 100644 -index 0000000..0c9826c ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_support.rpl -@@ -0,0 +1,127 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet support from the server -+; The query is sent to a server that does not reply with the edns-subnet -+; option. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 1 QUERY -+ENTRY_BEGIN -+REPLY RD DO -+SECTION QUESTION -+www.example.com. IN A -+ENTRY_END -+ -+; recursion happens here. -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_prezero.rpl b/unbound-1.16.2/testdata/subnet_prezero.rpl -new file mode 100644 -index 0000000..22cdfff ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_prezero.rpl -@@ -0,0 +1,155 @@ -+; subnet unit test -+server: -+ trust-anchor-signaling: no -+ send-client-subnet: 1.2.3.4 -+ send-client-subnet: 1.2.3.5 -+ target-fetch-policy: "0 0 0 0 0" -+ module-config: "subnetcache validator iterator" -+ qname-minimisation: no -+ minimal-responses: no -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnetcache source prefix zero from client. -+; In RFC7871 section-7.1.2 (para. 2). -+; It says that the recursor must send no EDNS subnet or its own address -+; in the EDNS subnet to the upstream server. And use that answer for the -+; source prefix length zero query. That type of query is for privacy. -+; The authority server is then going to use the resolver's IP, if any, to -+; tailor the answer to the query source address. -+ -+; ns.example.com -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+; reply with 0.0.0.0/0 in reply -+; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal -+; answers are NOERROR. -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA DO SERVFAIL -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; reply without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+ENTRY_END -+ -+; delegation answer for c10r.example.com, with subnet /0 -+ENTRY_BEGIN -+MATCH opcode subdomain ednsdata -+ADJUST copy_id copy_query -+REPLY QR DO SERVFAIL -+SECTION QUESTION -+c10r.example.com. IN NS -+SECTION AUTHORITY -+c10r.example.com. IN NS ns.c10r.example.com. -+SECTION ADDITIONAL -+ns.c10r.example.com. IN A 1.2.3.5 -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; delegation answer for c10r.example.com, without subnet -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR DO NOERROR -+SECTION QUESTION -+c10r.example.com. IN NS -+SECTION AUTHORITY -+c10r.example.com. IN NS ns.c10r.example.com. -+SECTION ADDITIONAL -+ns.c10r.example.com. IN A 1.2.3.5 -+ENTRY_END -+RANGE_END -+ -+; ns.c10r.example.com -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.5 -+ -+; reply with 0.0.0.0/0 in reply -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA DO SERVFAIL -+SECTION QUESTION -+star.c10r.example.com. IN A -+SECTION ANSWER -+star.c10r.example.com. IN A 1.2.3.6 -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; reply without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA DO NOERROR -+SECTION QUESTION -+star.c10r.example.com. IN A -+SECTION ANSWER -+star.c10r.example.com. IN A 1.2.3.6 -+ENTRY_END -+RANGE_END -+ -+; ask for www.example.com -+; server answers with CNAME to a delegation, that then -+; returns a /24 answer. -+STEP 1 QUERY -+ENTRY_BEGIN -+REPLY RD DO -+SECTION QUESTION -+www.example.com. IN A -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+star.c10r.example.com. IN A 1.2.3.6 -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_scopezero.rpl b/unbound-1.16.2/testdata/subnet_scopezero.rpl -new file mode 100644 -index 0000000..e006514 ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_scopezero.rpl -@@ -0,0 +1,439 @@ -+; scope of 0, if the query also had scope of 0, do not answer this -+; to everyone, but only for scope 0 queries. Otherwise can answer cached. -+ -+server: -+ target-fetch-policy: "0 0 0 0 0" -+ send-client-subnet: 1.2.3.4 -+ module-config: "subnetcache validator iterator" -+ verbosity: 4 -+ qname-minimisation: no -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet cache with scope zero queries and responses. -+ -+; the upstream server. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ;; we expect to receive empty -+HEX_EDNSDATA_END -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 0 11 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 20 31 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 40 51 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address 0.0.0.0/0 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 120 131 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address 127.2.0.0/24 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+; query for 127.0.0.0/24 -+STEP 1 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24 -+STEP 20 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 30 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0 -+STEP 40 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 50 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.0.0.0/24, again, it should be in cache. -+; and not from the scope 0 answer. -+STEP 60 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24, again, it should be in cache. -+STEP 80 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 90 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0, again. -+STEP 100 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 110 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; now a query for a /24 that gets an answer for a /0. -+STEP 120 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 02 00 ;127.2.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 -+STEP 130 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; the scope 0 answer is now used to answer queries from -+; query for 127.0.0.0/24 -+STEP 140 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+STEP 150 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl -new file mode 100644 -index 0000000..25df0dd ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl -@@ -0,0 +1,441 @@ -+; scope of 0, if the query also had scope of 0, do not answer this -+; to everyone, but only for scope 0 queries. Otherwise can answer cached. -+ -+server: -+ target-fetch-policy: "0 0 0 0 0" -+ send-client-subnet: 1.2.3.4 -+ module-config: "subnetcache validator iterator" -+ verbosity: 4 -+ qname-minimisation: no -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet cache with scope zero response without EDNS. -+ -+; the upstream server. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ;; we expect to receive empty -+HEX_EDNSDATA_END -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 0 11 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 20 31 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 40 51 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+;no EDNS in this answer. Tests if the back_parsed callback -+;is called to process the lack of edns contents. -+;HEX_EDNSDATA_BEGIN -+ ;00 08 ; OPC -+ ;00 04 ; option length -+ ;00 01 ; Family -+ ;00 00 ; source mask, scopemask -+ ; ; address 0.0.0.0/0 scope 0 -+;HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 120 131 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address 127.2.0.0/24 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+; query for 127.0.0.0/24 -+STEP 1 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24 -+STEP 20 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 30 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0 -+STEP 40 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 50 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.0.0.0/24, again, it should be in cache. -+; and not from the scope 0 answer. -+STEP 60 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24, again, it should be in cache. -+STEP 80 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 90 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0, again. -+STEP 100 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 110 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; now a query for a /24 that gets an answer for a /0. -+STEP 120 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 02 00 ;127.2.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 -+STEP 130 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; the scope 0 answer is now used to answer queries from -+; query for 127.0.0.0/24 -+STEP 140 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+STEP 150 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END diff --git a/SOURCES/unbound-1.24.2.tar.gz.asc b/SOURCES/unbound-1.24.2.tar.gz.asc new file mode 100644 index 0000000..f4a04ca --- /dev/null +++ b/SOURCES/unbound-1.24.2.tar.gz.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJIBAABCAAyFiEElI60IyLF0At5NA9dz/M0TZCHpJAFAmkm1EQUHGdlb3JnZUBu +bG5ldGxhYnMubmwACgkQz/M0TZCHpJBShg//WYjN8jarUIfeH1IhnnqxTBxZ5CvT +WUkgQy5laBIeWRE9l+rWdG7KRVjIH5wDLN9kuwSA6daO1owwxubrj20czkyaByFi +Mfb2V0FgpUwvfqXILEW0taEzQyGnzJy/UPV3nZXWWaLeRIrjVb5rNtazprQMlFSV +1OhCJX77BI1NNC87/I5HGPO5dAR8epe3+6tdXP29vDJHnkWkEvy17y8uTxDwRA/b +Yip9yiT7HbOHQsQhfFwQ747Tzc6CZ8XkRPLd2QNWA6tGaqQINDJv7N8/VqxqnjXF +wtpsRpn/qBx8m3T6u6/au0LiwnGUikmPyjsPZapIvlgP/BW33wU/HO2AxQWFO234 +5wdZ37BSchvHJFPtNJXX3Wak4FcWbe41GlP9dHCD74D/d1uG9DyeuMC5aoGStQZu +ldMzCoNwLKS4bfQyFsNA1rldinNRtoz7/Ac2Y9+Z6VhI6d/uqb+FBmenavvqQblz +bFccL0nQ4I4xjhGFqSjfTrQgwHQnyKKTToZzTSABqssG97m3F6twdrcZOqYCotLN +9ttXdwEwOUIpVD2UUbjS3LfZHBuQDjIETqgC89UZb6cOVzLbTFfnAQBDhFTGvqq5 +ohhAiZa9ePg8gXuziPtxp7AyQ+izvWESn7Af1yuXu315xuU7OG/7Wh1wyN2wjD5+ +vbIU556z7rrFT30= +=vyR7 +-----END PGP SIGNATURE----- diff --git a/SOURCES/unbound-1.25-tls-crypto-policy-default.patch b/SOURCES/unbound-1.25-tls-crypto-policy-default.patch new file mode 100644 index 0000000..7b532eb --- /dev/null +++ b/SOURCES/unbound-1.25-tls-crypto-policy-default.patch @@ -0,0 +1,61 @@ +From 87e5c3ccad91c72166ed72c7366d8681308d84a8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Mon, 9 Feb 2026 15:40:51 +0100 +Subject: [PATCH] Add a new build-time option for system TLS + +We want to use crypto-policy provided configuration always in our +builds. Allow changing the default of tls-use-system-policy-versions at +build time by a simple configure parameter. +--- + unbound-1.24.2/configure.ac | 11 +++++++++++ + unbound-1.24.2/util/config_file.c | 5 ++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/unbound-1.24.2/configure.ac b/unbound-1.24.2/configure.ac +index 6a43fd5..13c910f 100644 +--- a/unbound-1.24.2/configure.ac ++++ b/unbound-1.24.2/configure.ac +@@ -2067,6 +2067,17 @@ case "$enable_ipsecmod" in + ;; + esac + ++# check for system TLS preference if requested ++AC_ARG_ENABLE(system-tls, AS_HELP_STRING([--enable-system-tls],[Enable preference of system configured TLS socket options])) ++case "$enable_system_tls" in ++ yes) ++ AC_DEFINE([USE_SYSTEM_TLS], [1], [Define to 1 to prefer TLS crypto settings from the system.]) ++ ;; ++ no|*) ++ # nothing ++ ;; ++esac ++ + # check for ipset if requested + AC_ARG_ENABLE(ipset, AS_HELP_STRING([--enable-ipset],[enable ipset module])) + case "$enable_ipset" in +diff --git a/unbound-1.24.2/util/config_file.c b/unbound-1.24.2/util/config_file.c +index 1565b05..8079ec9 100644 +--- a/unbound-1.24.2/util/config_file.c ++++ b/unbound-1.24.2/util/config_file.c +@@ -71,6 +71,9 @@ + #ifdef HAVE_PWD_H + #include + #endif ++#ifndef USE_SYSTEM_TLS ++#define USE_SYSTEM_TLS 0 ++#endif + + /** from cfg username, after daemonize setup performed */ + uid_t cfg_uid = (uid_t)-1; +@@ -129,7 +132,7 @@ config_create(void) + cfg->tls_cert_bundle = NULL; + cfg->tls_win_cert = 0; + cfg->tls_use_sni = 1; +- cfg->tls_use_system_policy_versions = 0; ++ cfg->tls_use_system_policy_versions = USE_SYSTEM_TLS; + cfg->https_port = UNBOUND_DNS_OVER_HTTPS_PORT; + if(!(cfg->http_endpoint = strdup("/dns-query"))) goto error_exit; + cfg->http_max_streams = 100; +-- +2.52.0 + diff --git a/SOURCES/unbound-1.25-tls-crypto-policy.patch b/SOURCES/unbound-1.25-tls-crypto-policy.patch new file mode 100644 index 0000000..7f6f911 --- /dev/null +++ b/SOURCES/unbound-1.25-tls-crypto-policy.patch @@ -0,0 +1,483 @@ +From 315d29b33f9766e7ecb91fcfd4c1238d92aefd9e Mon Sep 17 00:00:00 2001 +From: Yorgos Thessalonikefs +Date: Mon, 22 Sep 2025 18:22:36 +0200 +Subject: [PATCH] - Fix #1346: [FR] Please allow back TLS 1.2. + 'tls-use-system-policy-versions' is introduced to allow Unbound to use any + system available TLS version when serving TLS. + +--- + unbound-1.24.2/daemon/remote.c | 2 +- + unbound-1.24.2/daemon/unbound.c | 2 +- + unbound-1.24.2/dnstap/unbound-dnstap-socket.c | 2 +- + unbound-1.24.2/doc/example.conf.in | 26 ++++++--- + unbound-1.24.2/doc/unbound.conf.5.in | 55 ++++++++++++++----- + unbound-1.24.2/doc/unbound.conf.rst | 32 ++++++++--- + unbound-1.24.2/util/config_file.c | 3 + + unbound-1.24.2/util/config_file.h | 2 + + unbound-1.24.2/util/configlexer.lex | 1 + + unbound-1.24.2/util/configparser.y | 11 ++++ + unbound-1.24.2/util/net_help.c | 54 +++++++++--------- + unbound-1.24.2/util/net_help.h | 8 ++- + unbound-1.24.2/winrc/win_svc.c | 4 +- + 13 files changed, 138 insertions(+), 64 deletions(-) + +diff --git a/unbound-1.24.2/daemon/remote.c b/unbound-1.24.2/daemon/remote.c +index d8ee7fa..06d1d34 100644 +--- a/unbound-1.24.2/daemon/remote.c ++++ b/unbound-1.24.2/daemon/remote.c +@@ -153,7 +153,7 @@ remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) + log_crypto_err("could not SSL_CTX_new"); + return 0; + } +- if(!listen_sslctx_setup(rc->ctx)) { ++ if(!listen_sslctx_setup(rc->ctx, cfg->tls_use_system_policy_versions)) { + return 0; + } + +diff --git a/unbound-1.24.2/daemon/unbound.c b/unbound-1.24.2/daemon/unbound.c +index 164d0fb..6888047 100644 +--- a/unbound-1.24.2/daemon/unbound.c ++++ b/unbound-1.24.2/daemon/unbound.c +@@ -473,7 +473,7 @@ setup_listen_sslctx(void** ctx, int is_dot, int is_doh, struct config_file* cfg) + cfg->tls_ciphers, cfg->tls_ciphersuites, + (cfg->tls_session_ticket_keys.first && + cfg->tls_session_ticket_keys.first->str[0] != 0), +- is_dot, is_doh))) { ++ is_dot, is_doh, cfg->tls_use_system_policy_versions))) { + fatal_exit("could not set up listen SSL_CTX"); + } + } +diff --git a/unbound-1.24.2/dnstap/unbound-dnstap-socket.c b/unbound-1.24.2/dnstap/unbound-dnstap-socket.c +index a01627d..c0d344c 100644 +--- a/unbound-1.24.2/dnstap/unbound-dnstap-socket.c ++++ b/unbound-1.24.2/dnstap/unbound-dnstap-socket.c +@@ -347,7 +347,7 @@ static struct tap_socket* tap_socket_new_tlsaccept(char* ip, + s->ev_cb = ev_cb; + s->data = data; + s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem, +- NULL, NULL, 0, 0, 0); ++ NULL, NULL, 0, 0, 0, 0); + if(!s->sslctx) { + log_err("could not create ssl context"); + free(s->ip); +diff --git a/unbound-1.24.2/doc/example.conf.in b/unbound-1.24.2/doc/example.conf.in +index cf10b85..ecdc65c 100644 +--- a/unbound-1.24.2/doc/example.conf.in ++++ b/unbound-1.24.2/doc/example.conf.in +@@ -950,21 +950,26 @@ server: + # https-port: 443 + # quic-port: 853 + ++ # Also serve tls on these port numbers (eg. 443, ...), by listing ++ # tls-additional-port: portno for each of the port numbers. ++ + # cipher setting for TLSv1.2 + # tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256" + # cipher setting for TLSv1.3 + # tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" + +- # Pad responses to padded queries received over TLS +- # pad-responses: yes +- +- # Padded responses will be padded to the closest multiple of this size. +- # pad-responses-block-size: 468 +- + # Use the SNI extension for TLS connections. Default is yes. + # Changing the value requires a reload. + # tls-use-sni: yes + ++ # Allow general-purpose version-flexible TLS server configuration that ++ # may be further restricted by the system's policy. ++ # Use only if you want to support legacy TLS client connections. ++ # Default is no and Unbound will only use the latest available TLS ++ # version. ++ # Changing the value requires a reload. ++ # tls-use-system-policy-versions: no ++ + # Add the secret file for TLS Session Ticket. + # Secret file must be 80 bytes of random data. + # First key use to encrypt and decrypt TLS session tickets. +@@ -985,15 +990,18 @@ server: + # and on other systems, the default openssl certificates + # tls-system-cert: no + ++ # Pad responses to padded queries received over TLS ++ # pad-responses: yes ++ ++ # Padded responses will be padded to the closest multiple of this size. ++ # pad-responses-block-size: 468 ++ + # Pad queries over TLS upstreams + # pad-queries: yes + + # Padded queries will be padded to the closest multiple of this size. + # pad-queries-block-size: 128 + +- # Also serve tls on these port numbers (eg. 443, ...), by listing +- # tls-additional-port: portno for each of the port numbers. +- + # HTTP endpoint to provide DNS-over-HTTPS service on. + # http-endpoint: "/dns-query" + +diff --git a/unbound-1.24.2/doc/unbound.conf.5.in b/unbound-1.24.2/doc/unbound.conf.5.in +index a5857ac..7ec7860 100644 +--- a/unbound-1.24.2/doc/unbound.conf.5.in ++++ b/unbound-1.24.2/doc/unbound.conf.5.in +@@ -1218,6 +1218,47 @@ Default: \(dq\(dq + .UNINDENT + .INDENT 0.0 + .TP ++.B tls\-use\-sni: \fI\fP ++Enable or disable sending the SNI extension on TLS connections. ++.sp ++\fBNOTE:\fP ++.INDENT 7.0 ++.INDENT 3.5 ++Changing the value requires a reload. ++.UNINDENT ++.UNINDENT ++.sp ++Default: yes ++.UNINDENT ++.INDENT 0.0 ++.TP ++.B tls\-use\-system\-policy\-versions: \fI\fP ++Enable or disable general\-puspose version\-flexible TLS server configuration ++when serving TLS. ++This will allow the whole list of available TLS versions provided by the ++crypto library, which may have been further restricted by the system\(aqs ++crypto policy. ++.sp ++By default Unbound only uses the latest available TLS version. ++.sp ++\fBCAUTION:\fP ++.INDENT 7.0 ++.INDENT 3.5 ++Use only if you want to support legacy TLS client connections. ++.UNINDENT ++.UNINDENT ++.sp ++\fBNOTE:\fP ++.INDENT 7.0 ++.INDENT 3.5 ++Changing the value requires a reload. ++.UNINDENT ++.UNINDENT ++.sp ++Default: no ++.UNINDENT ++.INDENT 0.0 ++.TP + .B pad\-responses: \fI\fP + If enabled, TLS serviced queries that contained an EDNS Padding option will + cause responses padded to the closest multiple of the size specified in +@@ -1251,20 +1292,6 @@ Default: 128 + .UNINDENT + .INDENT 0.0 + .TP +-.B tls\-use\-sni: \fI\fP +-Enable or disable sending the SNI extension on TLS connections. +-.sp +-\fBNOTE:\fP +-.INDENT 7.0 +-.INDENT 3.5 +-Changing the value requires a reload. +-.UNINDENT +-.UNINDENT +-.sp +-Default: yes +-.UNINDENT +-.INDENT 0.0 +-.TP + .B https\-port: \fI\fP + The port number on which to provide DNS\-over\-HTTPS service. + Only interfaces configured with that port number as @number get the HTTPS +diff --git a/unbound-1.24.2/doc/unbound.conf.rst b/unbound-1.24.2/doc/unbound.conf.rst +index d83816c..555b1c8 100644 +--- a/unbound-1.24.2/doc/unbound.conf.rst ++++ b/unbound-1.24.2/doc/unbound.conf.rst +@@ -1103,6 +1103,30 @@ These options are part of the **server:** clause. + Default: "" + + ++@@UAHL@unbound.conf@tls-use-sni@@: ** ++ Enable or disable sending the SNI extension on TLS connections. ++ ++ .. note:: Changing the value requires a reload. ++ ++ Default: yes ++ ++ ++@@UAHL@unbound.conf@tls-use-system-policy-versions@@: ** ++ Enable or disable general-puspose version-flexible TLS server configuration ++ when serving TLS. ++ This will allow the whole list of available TLS versions provided by the ++ crypto library, which may have been further restricted by the system's ++ crypto policy. ++ ++ By default Unbound only uses the latest available TLS version. ++ ++ .. caution:: Use only if you want to support legacy TLS client connections. ++ ++ .. note:: Changing the value requires a reload. ++ ++ Default: no ++ ++ + @@UAHL@unbound.conf@pad-responses@@: ** + If enabled, TLS serviced queries that contained an EDNS Padding option will + cause responses padded to the closest multiple of the size specified in +@@ -1132,14 +1156,6 @@ These options are part of the **server:** clause. + Default: 128 + + +-@@UAHL@unbound.conf@tls-use-sni@@: ** +- Enable or disable sending the SNI extension on TLS connections. +- +- .. note:: Changing the value requires a reload. +- +- Default: yes +- +- + @@UAHL@unbound.conf@https-port@@: ** + The port number on which to provide DNS-over-HTTPS service. + Only interfaces configured with that port number as @number get the HTTPS +diff --git a/unbound-1.24.2/util/config_file.c b/unbound-1.24.2/util/config_file.c +index 25c2b3e..1565b05 100644 +--- a/unbound-1.24.2/util/config_file.c ++++ b/unbound-1.24.2/util/config_file.c +@@ -129,6 +129,7 @@ config_create(void) + cfg->tls_cert_bundle = NULL; + cfg->tls_win_cert = 0; + cfg->tls_use_sni = 1; ++ cfg->tls_use_system_policy_versions = 0; + cfg->https_port = UNBOUND_DNS_OVER_HTTPS_PORT; + if(!(cfg->http_endpoint = strdup("/dns-query"))) goto error_exit; + cfg->http_max_streams = 100; +@@ -629,6 +630,7 @@ int config_set_option(struct config_file* cfg, const char* opt, + else S_STR("tls-ciphers:", tls_ciphers) + else S_STR("tls-ciphersuites:", tls_ciphersuites) + else S_YNO("tls-use-sni:", tls_use_sni) ++ else S_YNO("tls-use-system-policy-versions:", tls_use_system_policy_versions) + else S_NUMBER_NONZERO("https-port:", https_port) + else S_STR("http-endpoint:", http_endpoint) + else S_NUMBER_NONZERO("http-max-streams:", http_max_streams) +@@ -1181,6 +1183,7 @@ config_get_option(struct config_file* cfg, const char* opt, + else O_STR(opt, "tls-ciphers", tls_ciphers) + else O_STR(opt, "tls-ciphersuites", tls_ciphersuites) + else O_YNO(opt, "tls-use-sni", tls_use_sni) ++ else O_YNO(opt, "tls-use-system-policy-versions", tls_use_system_policy_versions) + else O_DEC(opt, "https-port", https_port) + else O_STR(opt, "http-endpoint", http_endpoint) + else O_UNS(opt, "http-max-streams", http_max_streams) +diff --git a/unbound-1.24.2/util/config_file.h b/unbound-1.24.2/util/config_file.h +index f77538b..59b4ae5 100644 +--- a/unbound-1.24.2/util/config_file.h ++++ b/unbound-1.24.2/util/config_file.h +@@ -148,6 +148,8 @@ struct config_file { + char* tls_ciphersuites; + /** if SNI is to be used */ + int tls_use_sni; ++ /** if all TLS versions can be used; based on system policy (if any) */ ++ int tls_use_system_policy_versions; + + /** port on which to provide DNS over HTTPS service */ + int https_port; +diff --git a/unbound-1.24.2/util/configlexer.lex b/unbound-1.24.2/util/configlexer.lex +index 0ba8d60..22f98e4 100644 +--- a/unbound-1.24.2/util/configlexer.lex ++++ b/unbound-1.24.2/util/configlexer.lex +@@ -262,6 +262,7 @@ tls-session-ticket-keys{COLON} { YDVAR(1, VAR_TLS_SESSION_TICKET_KEYS) } + tls-ciphers{COLON} { YDVAR(1, VAR_TLS_CIPHERS) } + tls-ciphersuites{COLON} { YDVAR(1, VAR_TLS_CIPHERSUITES) } + tls-use-sni{COLON} { YDVAR(1, VAR_TLS_USE_SNI) } ++tls-use-system-policy-versions{COLON} { YDVAR(1, VAR_TLS_USE_SYSTEM_POLICY_VERSIONS) } + https-port{COLON} { YDVAR(1, VAR_HTTPS_PORT) } + http-endpoint{COLON} { YDVAR(1, VAR_HTTP_ENDPOINT) } + http-max-streams{COLON} { YDVAR(1, VAR_HTTP_MAX_STREAMS) } +diff --git a/unbound-1.24.2/util/configparser.y b/unbound-1.24.2/util/configparser.y +index bef1fd3..bf9c196 100644 +--- a/unbound-1.24.2/util/configparser.y ++++ b/unbound-1.24.2/util/configparser.y +@@ -199,6 +199,7 @@ extern struct config_parser_state* cfg_parser; + %token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE + %token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK + %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI ++%token VAR_TLS_USE_SYSTEM_POLICY_VERSIONS + %token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 + %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE + %token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG VAR_RPZ_LOG_NAME +@@ -347,6 +348,7 @@ content_server: server_num_threads | server_verbosity | server_port | + server_tls_ciphersuites | server_tls_session_ticket_keys | + server_answer_cookie | server_cookie_secret | server_ip_ratelimit_cookie | + server_tls_use_sni | server_edns_client_string | ++ server_tls_use_system_policy_versions | + server_edns_client_string_opcode | server_nsid | + server_zonemd_permissive_mode | server_max_reuse_tcp_queries | + server_tcp_reuse_timeout | server_tcp_auth_query_timeout | +@@ -1155,6 +1157,15 @@ server_tls_use_sni: VAR_TLS_USE_SNI STRING_ARG + free($2); + } + ; ++server_tls_use_system_policy_versions: VAR_TLS_USE_SYSTEM_POLICY_VERSIONS STRING_ARG ++ { ++ OUTYY(("P(server_tls_use_system_policy_versions:%s)\n", $2)); ++ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) ++ yyerror("expected yes or no."); ++ else cfg_parser->cfg->tls_use_system_policy_versions = (strcmp($2, "yes")==0); ++ free($2); ++ } ++ ; + server_https_port: VAR_HTTPS_PORT STRING_ARG + { + OUTYY(("P(server_https_port:%s)\n", $2)); +diff --git a/unbound-1.24.2/util/net_help.c b/unbound-1.24.2/util/net_help.c +index 6ce0d91..426ace9 100644 +--- a/unbound-1.24.2/util/net_help.c ++++ b/unbound-1.24.2/util/net_help.c +@@ -1226,7 +1226,7 @@ setup_ticket_keys_cb(void* sslctx) + #endif /* HAVE_SSL */ + + int +-listen_sslctx_setup(void* ctxt) ++listen_sslctx_setup(void* ctxt, int use_system_versions) + { + #ifdef HAVE_SSL + SSL_CTX* ctx = (SSL_CTX*)ctxt; +@@ -1238,35 +1238,37 @@ listen_sslctx_setup(void* ctxt) + return 0; + } + #endif +- if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) +- != SSL_OP_NO_SSLv3){ +- log_crypto_err("could not set SSL_OP_NO_SSLv3"); +- return 0; +- } ++ if(!use_system_versions) { ++ if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) ++ != SSL_OP_NO_SSLv3){ ++ log_crypto_err("could not set SSL_OP_NO_SSLv3"); ++ return 0; ++ } + #if defined(SSL_OP_NO_TLSv1) && defined(SSL_OP_NO_TLSv1_1) +- /* if we have tls 1.1 disable 1.0 */ +- if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1) & SSL_OP_NO_TLSv1) +- != SSL_OP_NO_TLSv1){ +- log_crypto_err("could not set SSL_OP_NO_TLSv1"); +- return 0; +- } ++ /* if we have tls 1.1 disable 1.0 */ ++ if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1) & SSL_OP_NO_TLSv1) ++ != SSL_OP_NO_TLSv1){ ++ log_crypto_err("could not set SSL_OP_NO_TLSv1"); ++ return 0; ++ } + #endif + #if defined(SSL_OP_NO_TLSv1_1) && defined(SSL_OP_NO_TLSv1_2) +- /* if we have tls 1.2 disable 1.1 */ +- if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1) & SSL_OP_NO_TLSv1_1) +- != SSL_OP_NO_TLSv1_1){ +- log_crypto_err("could not set SSL_OP_NO_TLSv1_1"); +- return 0; +- } ++ /* if we have tls 1.2 disable 1.1 */ ++ if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1) & SSL_OP_NO_TLSv1_1) ++ != SSL_OP_NO_TLSv1_1){ ++ log_crypto_err("could not set SSL_OP_NO_TLSv1_1"); ++ return 0; ++ } + #endif + #if defined(SSL_OP_NO_TLSv1_2) && defined(SSL_OP_NO_TLSv1_3) +- /* if we have tls 1.3 disable 1.2 */ +- if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2) & SSL_OP_NO_TLSv1_2) +- != SSL_OP_NO_TLSv1_2){ +- log_crypto_err("could not set SSL_OP_NO_TLSv1_2"); +- return 0; +- } ++ /* if we have tls 1.3 disable 1.2 */ ++ if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2) & SSL_OP_NO_TLSv1_2) ++ != SSL_OP_NO_TLSv1_2){ ++ log_crypto_err("could not set SSL_OP_NO_TLSv1_2"); ++ return 0; ++ } + #endif ++ } + #if defined(SSL_OP_NO_RENEGOTIATION) + /* disable client renegotiation */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) & +@@ -1341,7 +1343,7 @@ listen_sslctx_setup_2(void* ctxt) + void* listen_sslctx_create(const char* key, const char* pem, + const char* verifypem, const char* tls_ciphers, + const char* tls_ciphersuites, int set_ticket_keys_cb, +- int is_dot, int is_doh) ++ int is_dot, int is_doh, int use_system_versions) + { + #ifdef HAVE_SSL + SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method()); +@@ -1359,7 +1361,7 @@ void* listen_sslctx_create(const char* key, const char* pem, + SSL_CTX_free(ctx); + return NULL; + } +- if(!listen_sslctx_setup(ctx)) { ++ if(!listen_sslctx_setup(ctx, use_system_versions)) { + SSL_CTX_free(ctx); + return NULL; + } +diff --git a/unbound-1.24.2/util/net_help.h b/unbound-1.24.2/util/net_help.h +index 278e370..7b8a206 100644 +--- a/unbound-1.24.2/util/net_help.h ++++ b/unbound-1.24.2/util/net_help.h +@@ -478,9 +478,11 @@ void log_cert(unsigned level, const char* str, void* cert); + /** + * Set SSL_OP_NOxxx options on SSL context to disable bad crypto + * @param ctxt: SSL_CTX* ++ * @param use_system_versions: rely on the system policy (if any) for allowed ++ * TLS versions + * @return false on failure. + */ +-int listen_sslctx_setup(void* ctxt); ++int listen_sslctx_setup(void* ctxt, int use_system_versions); + + /** + * Further setup of listening SSL context, after keys loaded. +@@ -499,12 +501,14 @@ void listen_sslctx_setup_2(void* ctxt); + * to be set. + * @param is_dot: if the TLS connection is for DoT to set the appropriate ALPN. + * @param is_doh: if the TLS connection is for DoH to set the appropriate ALPN. ++ * @param use_system_versions: rely on the system policy (if any) for allowed ++ * TLS versions + * return SSL_CTX* or NULL on failure (logged). + */ + void* listen_sslctx_create(const char* key, const char* pem, + const char* verifypem, const char* tls_ciphers, + const char* tls_ciphersuites, int set_ticket_keys_cb, +- int is_dot, int is_doh); ++ int is_dot, int is_doh, int use_system_versions); + + /** + * create SSL connect context +diff --git a/unbound-1.24.2/winrc/win_svc.c b/unbound-1.24.2/winrc/win_svc.c +index 429b045..6fca0c7 100644 +--- a/unbound-1.24.2/winrc/win_svc.c ++++ b/unbound-1.24.2/winrc/win_svc.c +@@ -369,7 +369,7 @@ service_init(int r, struct daemon** d, struct config_file** c) + cfg->tls_ciphers, cfg->tls_ciphersuites, + (cfg->tls_session_ticket_keys.first && + cfg->tls_session_ticket_keys.first->str[0] != 0), +- 1, 0))) { ++ 1, 0, cfg->tls_use_system_policy_versions))) { + fatal_exit("could not set up listen SSL_CTX"); + } + #ifdef HAVE_NGHTTP2_NGHTTP2_H +@@ -379,7 +379,7 @@ service_init(int r, struct daemon** d, struct config_file** c) + cfg->tls_ciphers, cfg->tls_ciphersuites, + (cfg->tls_session_ticket_keys.first && + cfg->tls_session_ticket_keys.first->str[0] != 0), +- 0, 1))) { ++ 0, 1, cfg->tls_use_system_policy_versions))) { + fatal_exit("could not set up listen doh SSL_CTX"); + } + } +-- +2.52.0 + diff --git a/SOURCES/unbound-fedora-config.patch b/SOURCES/unbound-fedora-config.patch new file mode 100644 index 0000000..6fc5649 --- /dev/null +++ b/SOURCES/unbound-fedora-config.patch @@ -0,0 +1,113 @@ +From a8be97bada623287cdd911c7a4549bbceef9fea0 Mon Sep 17 00:00:00 2001 +From: Tomas Korbar +Date: Tue, 4 Feb 2025 09:48:12 +0100 +Subject: [PATCH] Customize unbound.conf for Fedora defaults + +Set some Fedora/RHEL specific changes to example configuration file. By +patching upstream provided config file we would not need to manually +update external copy in source RPM. +--- + unbound-1.24.2/doc/example.conf.in | 33 ++++++++++++++++++++++++++++-- + 1 file changed, 31 insertions(+), 2 deletions(-) + +diff --git a/unbound-1.24.2/doc/example.conf.in b/unbound-1.24.2/doc/example.conf.in +index fda565c..cf10b85 100644 +--- a/unbound-1.24.2/doc/example.conf.in ++++ b/unbound-1.24.2/doc/example.conf.in +@@ -51,11 +51,19 @@ server: + # specify 0.0.0.0 and ::0 to bind to all available interfaces. + # specify every interface[@port] on a new 'interface:' labelled line. + # The listen interfaces are not changed on reload, only on restart. ++ # interface: 0.0.0.0 ++ # interface: ::0 + # interface: 192.0.2.153 + # interface: 192.0.2.154 + # interface: 192.0.2.154@5003 + # interface: 2001:DB8::5 + # interface: eth0@5003 ++ # ++ # for dns over tls and raw dns over port 80 ++ # interface: 0.0.0.0@443 ++ # interface: ::0@443 ++ # interface: 0.0.0.0@80 ++ # interface: ::0@80 + + # enable this feature to copy the source address of queries to reply. + # Socket options are not supported on all platforms. experimental. +@@ -295,6 +303,8 @@ server: + # nat64-prefix: 64:ff9b::0/96 + + # Enable UDP, "yes" or "no". ++ # NOTE: if setting up an Unbound on tls443 for public use, you might want to ++ # disable UDP to avoid being used in DNS amplification attacks. + # do-udp: yes + + # Enable TCP, "yes" or "no". +@@ -330,6 +340,9 @@ server: + # can be dropped. Default is 0, disabled. In seconds, such as 3. + # sock-queue-timeout: 0 + ++ # Fedora note: do not activate this - not compiled in because ++ # it causes frequent unbound crashes. Also, socket activation ++ # is bad when you have things like dnsmasq also running with libvirt. + # Use systemd socket activation for UDP, TCP, and control sockets. + # use-systemd: no + +@@ -919,6 +932,8 @@ server: + # you need to do the reverse notation yourself. + # local-data-ptr: "192.0.2.3 www.example.com" + ++ include: /etc/unbound/local.d/*.conf ++ + # tag a localzone with a list of tag names (in "" with spaces between) + # local-zone-tag: "example.com" "tag2 tag3" + +@@ -929,8 +944,8 @@ server: + # the TLS stream, and over HTTPS using HTTP/2 as specified in RFC8484. + # Give the certificate to use and private key. + # default is "" (disabled). requires restart to take effect. +- # tls-service-key: "path/to/privatekeyfile.key" +- # tls-service-pem: "path/to/publiccertfile.pem" ++ # tls-service-key: "/etc/unbound/unbound_server.key" ++ # tls-service-pem: "/etc/unbound/unbound_server.pem" + # tls-port: 853 + # https-port: 443 + # quic-port: 853 +@@ -1184,6 +1199,12 @@ remote-control: + # unbound-control certificate file. + # control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem" + ++# Default Fedora settings ++include: "@UNBOUND_SHARE_DIR@/fedora-defaults.conf" ++ ++# Stub and Forward zones ++include: "@sysconfdir@/unbound/conf.d/*.conf" ++ + # Stub zones. + # Create entries like below, to make all queries for 'example.com' and + # 'example.org' go to the given list of nameservers. list zero or more +@@ -1204,6 +1225,10 @@ remote-control: + # name: "example.org" + # stub-host: ns.example.com. + ++# You can now also dynamically create and delete stub-zone's using ++# unbound-control stub_add domain.com 1.2.3.4 5.6.7.8 ++# unbound-control stub_remove domain.com 1.2.3.4 5.6.7.8 ++ + # Forward zones + # Create entries like below, to make all queries for 'example.com' and + # 'example.org' go to the given list of servers. These servers have to handle +@@ -1221,6 +1246,10 @@ remote-control: + # forward-zone: + # name: "example.org" + # forward-host: fwd.example.com ++# ++# You can now also dynamically create and delete forward-zone's using ++# unbound-control forward_add domain.com 1.2.3.4 5.6.7.8 ++# unbound-control forward_remove domain.com 1.2.3.4 5.6.7.8 + + # Authority zones + # The data for these zones is kept locally, from a file or downloaded. +-- +2.52.0 + diff --git a/SPECS/unbound.spec b/SPECS/unbound.spec index 7ac5e05..8d9e8d9 100644 --- a/SPECS/unbound.spec +++ b/SPECS/unbound.spec @@ -6,6 +6,7 @@ %bcond_without doh %global _hardened_build 1 +%global forgeurl https://github.com/NLnetLabs/%{name} #%%global extra_version rc1 @@ -29,8 +30,8 @@ Summary: Validating, recursive, and caching DNS(SEC) resolver Name: unbound -Version: 1.16.2 -Release: 21%{?extra_version:.%{extra_version}}%{?dist} +Version: 1.24.2 +Release: 2%{?extra_version:.%{extra_version}}%{?dist} License: BSD Url: https://nlnetlabs.nl/projects/unbound/ Source: https://nlnetlabs.nl/downloads/%{name}/%{name}-%{version}%{?extra_version}.tar.gz @@ -52,38 +53,31 @@ Source16: unbound-munin.README Source17: unbound-anchor.service Source18: https://nlnetlabs.nl/downloads/%{name}/%{name}-%{version}%{?extra_version}.tar.gz.asc Source19: http://keys.gnupg.net/pks/lookup?op=get&search=0x9F6F1C2D7E045F8D#/wouter.nlnetlabs.nl.key +Source20: https://nlnetlabs.nl/downloads/keys/Yorgos.asc Source21: remote-control.conf Source22: unbound-local-root.conf Source23: module-setup.sh Source24: unbound-initrd.conf Source25: unbound.sysusers Source26: unbound-as112-networks.conf +Source27: tmpfiles-unbound-libs.conf -# https://github.com/NLnetLabs/unbound/commit/137719522a8ea5b380fbb6206d2466f402f5b554 -Patch1: unbound-1.16-CVE-2022-3204.patch -# https://nlnetlabs.nl/downloads/unbound/patch_CVE-2023-50387_CVE-2023-50868.diff -Patch4: unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch -# https://github.com/NLnetLabs/unbound/commit/6d1e61173 -Patch5: unbound-1.16-control-t-flag.patch -# https://github.com/NLnetLabs/unbound/commit/b7c61d7cc256d6a174e6179622c7fa968272c259 -Patch6: unbound-1.21-CVE-2024-8508.patch -# https://github.com/NLnetLabs/unbound/commit/b48958c983f60af40358cca168c403e57bde30d2 -Patch7: unbound-1.16-control-key-perms.patch -# The patch for CVE-2025-5994 requires certain changes fixing bugs in subnet module -# that is why we have to backport these commits. They have their respective tests -# backported with them. -# https://github.com/NLnetLabs/unbound/commit/0f08cc6d5577ad4747749c55229e16df8711ee32 -# https://github.com/NLnetLabs/unbound/commit/6d0812b56731af130e8bc7e1572388934beb9b3b -# https://github.com/NLnetLabs/unbound/commit/be626f7c5330dc414a582a04b537ea79d5c452fb -# https://github.com/NLnetLabs/unbound/commit/5bf82f246481098a6473f296b21fc1229d276c0f -# https://github.com/NLnetLabs/unbound/commit/a1150078f29e14b36c8e4d9d05a263a5e6abbc5b -Patch8: unbound-1.23.1-CVE-2025-5994.patch +# Downstream configuration changes +Patch1: unbound-fedora-config.patch +# https://github.com/NLnetLabs/unbound/pull/1349 +Patch2: %{forgeurl}/pull/1349.patch#/unbound-1.25-tls-crypto-policy.patch +# https://github.com/NLnetLabs/unbound/pull/1401 +Patch3: %{forgeurl}/pull/1401.patch#/unbound-1.25-tls-crypto-policy-default.patch -BuildRequires: gcc, make -BuildRequires: flex, openssl-devel -BuildRequires: libevent-devel expat-devel +BuildRequires: gcc +BuildRequires: make +BuildRequires: flex +BuildRequires: byacc +BuildRequires: openssl-devel +BuildRequires: libevent-devel +BuildRequires: expat-devel BuildRequires: pkgconfig -%if 0%{?fedora} +%if 0%{?fedora} || 0%{?rhel} >= 10 BuildRequires: gnupg2 %endif %if 0%{with_python2} @@ -191,8 +185,8 @@ Unbound dracut module allowing use of Unbound for name resolution in initramfs. %prep -%if 0%{?fedora} -%gpgverify -k 19 -s 18 -d 0 +%if 0%{?fedora} || 0%{?rhel} >= 10 +%{gpgverify} --keyring='%{SOURCE20}' --signature='%{SOURCE18}' --data='%{SOURCE0}' %endif %global pkgname %{name}-%{version}%{?extra_version} @@ -212,7 +206,7 @@ pushd %{pkgname} %autopatch -p2 # only for snapshots -autoreconf -iv +autoreconf -fiv # copy common doc files - after here, since it may be patched cp -pr doc pythonmod libunbound ../ @@ -236,7 +230,8 @@ cp -a %{dir_primary} %{dir_secondary} --with-pidfile=%{_rundir}/%{name}/%{name}.pid \\\ --enable-sha2 --disable-gost --enable-ecdsa \\\ --with-rootkey-file=%{_sharedstatedir}/unbound/root.key \\\ - --enable-linux-ip-local-port-range --disable-sha1 + --enable-linux-ip-local-port-range --disable-sha1 \\\ + --enable-system-tls pushd %{dir_primary} @@ -314,18 +309,19 @@ done pushd %{dir_primary} # install streamtcp man page -install -m 0644 testcode/streamtcp.1 %{buildroot}/%{_mandir}/man1/unbound-streamtcp.1 -install -D -m 0644 contrib/libunbound.pc %{buildroot}/%{_libdir}/pkgconfig/libunbound.pc +install -p -m 0644 testcode/streamtcp.1 %{buildroot}/%{_mandir}/man1/unbound-streamtcp.1 +install -p -D -m 0644 contrib/libunbound.pc %{buildroot}/%{_libdir}/pkgconfig/libunbound.pc popd # Install tmpfiles.d config install -d -m 0755 %{buildroot}%{_tmpfilesdir} %{buildroot}%{_sharedstatedir}/unbound -install -m 0644 %{SOURCE8} %{buildroot}%{_tmpfilesdir}/unbound.conf +install -p -m 0644 %{SOURCE8} %{buildroot}%{_tmpfilesdir}/unbound.conf +install -p -m 0644 %{SOURCE27} %{buildroot}%{_tmpfilesdir}/unbound-libs.conf # install root - we keep a copy of the root key in old location, # in case user has changed the configuration and we wouldn't update it there -install -m 0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/unbound/ -install -m 0644 %{SOURCE13} %{buildroot}%{_sharedstatedir}/unbound/root.key +install -p -m 0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/unbound/ +install -p -m 0644 %{SOURCE13} %{buildroot}%{_sharedstatedir}/unbound/root.key # local root zone fetch to separated configuration file install -p -m 0644 %{SOURCE22} %{buildroot}%{_sysconfdir}/unbound/ @@ -492,6 +488,7 @@ popd %license doc/LICENSE %attr(0755,root,root) %dir %{_sysconfdir}/%{name} %{_sysusersdir}/%{name}.conf +%attr(0644,root,root) %{_tmpfilesdir}/unbound-libs.conf %{_sbindir}/unbound-anchor %{_libdir}/libunbound.so.* %{_mandir}/man8/unbound-anchor* @@ -501,7 +498,7 @@ popd %{_unitdir}/unbound-anchor.timer %{_unitdir}/unbound-anchor.service %dir %attr(0755,unbound,unbound) %{_sharedstatedir}/%{name} -%attr(0644,unbound,unbound) %config %{_sharedstatedir}/%{name}/root.key +%attr(0644,unbound,unbound) %verify(not md5 mtime size) %config %{_sharedstatedir}/%{name}/root.key # just left for backwards compat with user changed unbound.conf files - format is different! %attr(0644,root,root) %config %{_sysconfdir}/%{name}/root.key @@ -509,6 +506,20 @@ popd %{_prefix}/lib/dracut/modules.d/99unbound %changelog +* Mon Feb 09 2026 Petr Menšík - 1.24.2-2 +- Switch TLS configuration to follow TLS sockets by crypto-policy again + (RHEL-147860) +- Change the default of tls-use-system-policy-versions at build-time + +* Tue Nov 11 2025 Petr Menšík - 1.16.2-24 +- Add new root key 38696 (RHEL-77716) + +* Tue Nov 11 2025 Petr Menšík - 1.16.2-23 +- Do not verify root.key in libs (RHEL-64339) + +* Tue Nov 11 2025 Petr Menšík - 1.16.2-22 +- Create root key if missing automatically (RHEL-127540) + * Mon Jul 28 2025 Tomas Korbar - 1.16.2-21 - Fix RebirthDay Attack (CVE-2025-5994) - Resolves: RHEL-104129