From: Bob Halley Date: Tue, 26 Dec 2023 19:12:39 +0000 (-0800) Subject: Pad if needed when making a response. (#1026) X-Git-Tag: v2.5.0rc1~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1e7389f1fc1f2f7d405553c32525ee21b94c3a45;p=thirdparty%2Fdnspython.git Pad if needed when making a response. (#1026) --- diff --git a/dns/message.py b/dns/message.py index 8887f73b..21eaaf2f 100644 --- a/dns/message.py +++ b/dns/message.py @@ -826,6 +826,8 @@ class Message: if request_payload is None: request_payload = payload self.request_payload = request_payload + if pad < 0: + raise ValueError("pad must be non-negative") self.pad = pad @property @@ -1817,30 +1819,34 @@ def make_response( our_payload: int = 8192, fudge: int = 300, tsig_error: int = 0, + pad: Optional[int] = None, ) -> Message: """Make a message which is a response for the specified query. - The message returned is really a response skeleton; it has all - of the infrastructure required of a response, but none of the - content. + The message returned is really a response skeleton; it has all of the infrastructure + required of a response, but none of the content. - The response's question section is a shallow copy of the query's - question section, so the query's question RRsets should not be - changed. + The response's question section is a shallow copy of the query's question section, + so the query's question RRsets should not be changed. *query*, a ``dns.message.Message``, the query to respond to. *recursion_available*, a ``bool``, should RA be set in the response? - *our_payload*, an ``int``, the payload size to advertise in EDNS - responses. + *our_payload*, an ``int``, the payload size to advertise in EDNS responses. *fudge*, an ``int``, the TSIG time fudge. *tsig_error*, an ``int``, the TSIG error. - Returns a ``dns.message.Message`` object whose specific class is - appropriate for the query. For example, if query is a - ``dns.update.UpdateMessage``, response will be too. + *pad*, a non-negative ``int`` or ``None``. If 0, the default, do not pad; otherwise + if not ``None`` add padding bytes to make the message size a multiple of *pad*. + Note that if padding is non-zero, an EDNS PADDING option will always be added to the + message. If ``None``, add padding following RFC 8467, namely if the request is + padded, pad the response to 468 otherwise do not pad. + + Returns a ``dns.message.Message`` object whose specific class is appropriate for the + query. For example, if query is a ``dns.update.UpdateMessage``, response will be + too. """ if query.flags & dns.flags.QR: @@ -1853,7 +1859,13 @@ def make_response( response.set_opcode(query.opcode()) response.question = list(query.question) if query.edns >= 0: - response.use_edns(0, 0, our_payload, query.payload) + if pad is None: + # Set response padding per RFC 8467 + pad = 0 + for option in query.options: + if option.otype == dns.edns.OptionType.PADDING: + pad = 468 + response.use_edns(0, 0, our_payload, query.payload, pad=pad) if query.had_tsig: response.use_tsig( query.keyring, diff --git a/doc/rfc.rst b/doc/rfc.rst index 7e4da5d4..27afc261 100644 --- a/doc/rfc.rst +++ b/doc/rfc.rst @@ -60,12 +60,19 @@ Core RFCs `RFC 6891 `_ EDNS (version 0) +`RFC 7830 `_ + The EDNS(0) Padding Option + `RFC 8020 `_ Clarification on the meaning of NXDOMAIN. +`RFC 8467 `_ + Padding Policies for Extension Mechanisms for DNS (EDNS(0)) + `RFC 8914 `_ Extended DNS Errors + DNSSEC RFCs ----------- diff --git a/tests/test_message.py b/tests/test_message.py index 0ee53983..93c8aafd 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -16,23 +16,22 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -import unittest import binascii +import unittest -import dns.exception import dns.edns +import dns.exception import dns.flags import dns.message import dns.name import dns.rdataclass import dns.rdatatype -import dns.rrset -import dns.tsig -import dns.update import dns.rdtypes.ANY.OPT import dns.rdtypes.ANY.TSIG +import dns.rrset +import dns.tsig import dns.tsigkeyring - +import dns.update from tests.util import here query_text = """id 1234 @@ -882,6 +881,24 @@ www.dnspython.org. 300 IN A 1.2.3.4 self.assertIsNotNone(q2.tsig) self.assertEqual(q, q2) + def test_response_padding(self): + q = dns.message.make_query("www.example", "a", use_edns=0, pad=128) + w = q.to_wire() + self.assertEqual(len(w), 128) + # We need to go read the wire as the padding isn't instantiated in q. + pq = dns.message.from_wire(w) + r = dns.message.make_response(pq) + assert r.pad == 468 + r = dns.message.make_response(pq, pad=0) + assert r.pad == 0 + r = dns.message.make_response(pq, pad=40) + assert r.pad == 40 + q = dns.message.make_query("www.example", "a", use_edns=0, pad=0) + w = q.to_wire() + pq = dns.message.from_wire(w) + r = dns.message.make_response(pq) + assert r.pad == 0 + def test_prefer_truncation_answer(self): q = dns.message.make_query("www.example", "a") rrs = [