Allow TXT record over 255 characters if split

As defined in RFC1035 section 3.3.14 TXT-DATA
can be one or more <character-strings>s.
Before this commit Designate threw errors
when saving TXT records that are split
into multiple strings because validations on
the field did not consider allowing this
in a single DNS record as per RFC7208
section 3.3.

This patch allows longer TXT record data
but only if it is split according to RFC
definitions mentioned above. If data
is made of more <character-string>s,
each one is individually validated with
the same validations as if the data was
not split.

Closes-Bug: 1595265
Change-Id: I4e3e51b32ab01efc4202c297708eff5a2e2b4985
Signed-off-by: Emanuel Andrecut (emanuel.andrecut@fleio.com)
This commit is contained in:
Emanuel Andrecut 2021-10-20 11:48:18 +03:00
parent 771197c2f3
commit 03a5d5d74e
4 changed files with 72 additions and 3 deletions

View File

@ -26,14 +26,23 @@ class TXT(Record):
Defined in: RFC1035
"""
fields = {
'txt_data': fields.TxtField(maxLength=255)
'txt_data': fields.TxtField()
}
def _to_string(self):
return self.txt_data
def _from_string(self, value):
if (not value.startswith('"') and not value.endswith('"')):
@staticmethod
def _is_wrapped_in_double_quotes(value):
return value.startswith('"') and value.endswith('"')
def _validate_record_single_string(self, value):
if len(value) > 255:
err = ("Any TXT record string exceeding "
"255 characters has to be split.")
raise InvalidObject(err)
if not self._is_wrapped_in_double_quotes(value):
# value with spaces should be quoted as per RFC1035 5.1
for element in value:
if element.isspace():
@ -50,6 +59,29 @@ class TXT(Record):
"backslash.")
raise InvalidObject(err)
def _from_string(self, value):
if len(value) > 255:
# expecting record containing multiple strings as
# per rfc7208 3.3 and rfc1035 3.3.14
stripped_value = value.strip('"')
if (not self._is_wrapped_in_double_quotes(value) and
'" "' not in stripped_value):
err = ("TXT record strings over 255 characters "
"have to be split into multiple strings "
"wrapped in double quotes.")
raise InvalidObject(err)
record_strings = stripped_value.split('" "')
for record_string in record_strings:
# add back the delimiting quotes after
# strip and split for each string
record_string = '"{}"'.format(record_string)
# further validate each string individually
self._validate_record_single_string(value=record_string)
else:
# validate single TXT record string
self._validate_record_single_string(value=value)
self.txt_data = value
# The record type is defined in the RFC. This will be used when the record

View File

@ -567,6 +567,17 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self._assert_exception('invalid_object', 400,
self.client.put_json, url, body)
def test_create_txt_record_multiple_strings(self):
# create TXT record with string split in 2
new_zone = self.create_zone(name='example.net.')
recordset = self.create_recordset(new_zone, 'TXT')
self.create_record(new_zone, recordset)
record = '"{}" "{}"'.format('a' * 250, 'a' * 250)
body = {'description': 'Tester', 'records': [record]}
url = '/zones/%s/recordsets/%s' % (recordset['zone_id'],
recordset['id'])
self.client.put_json(url, body, status=202)
def test_update_recordset_with_record_clear(self):
# Create a recordset with one record
recordset = self.create_recordset(self.zone, 'A')

View File

@ -34,3 +34,13 @@ class RRDataTXTTest(oslotest.base.BaseTestCase):
'Provided object does not match schema',
record.validate
)
def test_multiple_strings_one_record(self):
# these quotes do not have to be escaped as
# per rfc7208 3.3 and rfc1035 3.3.14
record = objects.TXT(data='"foo" "bar"')
self.assertRaisesRegex(
exceptions.InvalidObject,
'Provided object does not match schema',
record.validate
)

View File

@ -149,6 +149,22 @@ because it is the default:
$ dig @ns1.example.net www.example.org. +short
192.0.2.1
If you want to construct a ``TXT`` record that exceeds the 255-octet
maximum length of a character-string, it has to be split into
multiple strings as defined in RFC7208 section 3.3. For example,
``"v=DKIM1; .... firstsecond string..."`` can become
``"v=DKIM1; .... first" "second string..."``. If you provide a record
data with less than 255 characters, it will be treated as a
single character-string and validated for empty spaces outside quotes
and unescaped double quotation marks as in RFC1035 section 5.1.
For example, to create a ``TXT`` record made of one string of 410
characters you can split it into 2 to like this:
.. code-block:: console
$ openstack recordset create --type TXT --record '"210 characters string" "200 characters string"' example.org. _domainkey
Updating a recordset
--------------------