diff options
author | Emanuel Andrecut <emanuel.andrecut@fleio.com> | 2021-10-20 11:48:18 +0300 |
---|---|---|
committer | Emanuel Andrecut <emanuel.andrecut@fleio.com> | 2021-10-21 19:47:44 +0300 |
commit | 03a5d5d74eb5de3f37b2e7ac5f28ae139ae436a5 (patch) | |
tree | 36491f3e13fd33d5072871ce08a8cea004c1cb8b /designate | |
parent | 771197c2f31209fe135888aef6f2828af4889653 (diff) | |
download | designate-03a5d5d74eb5de3f37b2e7ac5f28ae139ae436a5.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)
Diffstat (limited to 'designate')
-rw-r--r-- | designate/objects/rrdata_txt.py | 38 | ||||
-rw-r--r-- | designate/tests/test_api/test_v2/test_recordsets.py | 11 | ||||
-rw-r--r-- | designate/tests/unit/objects/test_rrdata_txt.py | 10 |
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 + ) |