Skip to content

Commit

Permalink
Pad if needed when making a response. (#1026)
Browse files Browse the repository at this point in the history
  • Loading branch information
rthalley committed Dec 26, 2023
1 parent f03f3b7 commit 1e7389f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 18 deletions.
36 changes: 24 additions & 12 deletions dns/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ def use_edns(
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
Expand Down Expand Up @@ -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:
Expand All @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions doc/rfc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,19 @@ Core RFCs
`RFC 6891 <https://tools.ietf.org/html/rfc6891>`_
EDNS (version 0)

`RFC 7830 <https://tools.ietf.org/html/rfc7830.html>`_
The EDNS(0) Padding Option

`RFC 8020 <https://tools.ietf.org/html/rfc8020>`_
Clarification on the meaning of NXDOMAIN.

`RFC 8467 <https://tools.ietf.org/html/rfc8467>`_
Padding Policies for Extension Mechanisms for DNS (EDNS(0))

`RFC 8914 <https://tools.ietf.org/html/rfc8914.html>`_
Extended DNS Errors


DNSSEC RFCs
-----------

Expand Down
29 changes: 23 additions & 6 deletions tests/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -882,6 +881,24 @@ def test_padding_with_tsig_and_option(self):
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 = [
Expand Down

0 comments on commit 1e7389f

Please sign in to comment.