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:
parent
771197c2f3
commit
03a5d5d74e
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
--------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user