summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmanuel Andrecut <emanuel.andrecut@fleio.com>2021-10-20 11:48:18 +0300
committerMichael Johnson <johnsomor@gmail.com>2022-02-22 22:46:01 +0000
commit0323c4244929aaf199ecbb7c8d32138764029cc2 (patch)
tree245dfc4adb7560a9b7d7936ae9cc63f39a996878
parent63d6672befb2225d844388fe43f857b0f3dbb8b6 (diff)
downloaddesignate-0323c4244929aaf199ecbb7c8d32138764029cc2.tar.gz
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) (cherry picked from commit 03a5d5d74eb5de3f37b2e7ac5f28ae139ae436a5) (cherry picked from commit 5e9cca1b02a8e115e0f4cb471b180427db4c3993)
-rw-r--r--designate/objects/rrdata_txt.py38
-rw-r--r--designate/tests/test_api/test_v2/test_recordsets.py11
-rw-r--r--designate/tests/unit/objects/test_rrdata_txt.py10
3 files changed, 56 insertions, 3 deletions
diff --git a/designate/objects/rrdata_txt.py b/designate/objects/rrdata_txt.py
index 44e13fff..9fcec720 100644
--- a/designate/objects/rrdata_txt.py
+++ b/designate/objects/rrdata_txt.py
@@ -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
diff --git a/designate/tests/test_api/test_v2/test_recordsets.py b/designate/tests/test_api/test_v2/test_recordsets.py
index 850ac086..9b96157c 100644
--- a/designate/tests/test_api/test_v2/test_recordsets.py
+++ b/designate/tests/test_api/test_v2/test_recordsets.py
@@ -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')
diff --git a/designate/tests/unit/objects/test_rrdata_txt.py b/designate/tests/unit/objects/test_rrdata_txt.py
index f41cd930..22274816 100644
--- a/designate/tests/unit/objects/test_rrdata_txt.py
+++ b/designate/tests/unit/objects/test_rrdata_txt.py
@@ -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
+ )