Merge "Allow TXT record over 255 characters if split"
This commit is contained in:
@@ -26,14 +26,23 @@ class TXT(Record):
|
|||||||
Defined in: RFC1035
|
Defined in: RFC1035
|
||||||
"""
|
"""
|
||||||
fields = {
|
fields = {
|
||||||
'txt_data': fields.TxtField(maxLength=255)
|
'txt_data': fields.TxtField()
|
||||||
}
|
}
|
||||||
|
|
||||||
def _to_string(self):
|
def _to_string(self):
|
||||||
return self.txt_data
|
return self.txt_data
|
||||||
|
|
||||||
def _from_string(self, value):
|
@staticmethod
|
||||||
if (not value.startswith('"') and not value.endswith('"')):
|
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
|
# value with spaces should be quoted as per RFC1035 5.1
|
||||||
for element in value:
|
for element in value:
|
||||||
if element.isspace():
|
if element.isspace():
|
||||||
@@ -50,6 +59,29 @@ class TXT(Record):
|
|||||||
"backslash.")
|
"backslash.")
|
||||||
raise InvalidObject(err)
|
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
|
self.txt_data = value
|
||||||
|
|
||||||
# The record type is defined in the RFC. This will be used when the record
|
# 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._assert_exception('invalid_object', 400,
|
||||||
self.client.put_json, url, body)
|
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):
|
def test_update_recordset_with_record_clear(self):
|
||||||
# Create a recordset with one record
|
# Create a recordset with one record
|
||||||
recordset = self.create_recordset(self.zone, 'A')
|
recordset = self.create_recordset(self.zone, 'A')
|
||||||
|
@@ -34,3 +34,13 @@ class RRDataTXTTest(oslotest.base.BaseTestCase):
|
|||||||
'Provided object does not match schema',
|
'Provided object does not match schema',
|
||||||
record.validate
|
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
|
$ dig @ns1.example.net www.example.org. +short
|
||||||
192.0.2.1
|
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
|
Updating a recordset
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user