summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2021-12-03 02:13:43 +0000
committerGerrit Code Review <review@openstack.org>2021-12-03 02:13:43 +0000
commit1356945484b38ef2a0b8888cc6547a681247a907 (patch)
tree560ba5024593ce244d337860210e838ad0c736d9
parent8634d531f2d993901527ecc67ddc40eb9dc0a575 (diff)
parent5e9cca1b02a8e115e0f4cb471b180427db4c3993 (diff)
downloaddesignate-1356945484b38ef2a0b8888cc6547a681247a907.tar.gz
Merge "Allow TXT record over 255 characters if split" into stable/xena
-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
-rw-r--r--doc/source/user/manage-recordsets.rst16
4 files changed, 72 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
+ )
diff --git a/doc/source/user/manage-recordsets.rst b/doc/source/user/manage-recordsets.rst
index 71b4b52f..d50d052e 100644
--- a/doc/source/user/manage-recordsets.rst
+++ b/doc/source/user/manage-recordsets.rst
@@ -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
--------------------