Browse Source

Implement "chunked" TXT/SPF value support for long values

This implements it transparently at Record level. Providers that need things to
be chunked (seems to just be Route53 an Dyn) switch to use `chunked_values`, but
everything else can stick with `values`. I've run through each provider I have
access to verifying that things operate as expected/required. OVH and Azure are
untested.
pull/115/head
Ross McFarland 8 years ago
parent
commit
70120bedc8
No known key found for this signature in database GPG Key ID: 61C10C4FC8FE4A89
4 changed files with 81 additions and 15 deletions
  1. +1
    -1
      octodns/provider/dyn.py
  2. +1
    -2
      octodns/provider/route53.py
  3. +23
    -12
      octodns/record.py
  4. +56
    -0
      tests/test_octodns_record.py

+ 1
- 1
octodns/provider/dyn.py View File

@ -455,7 +455,7 @@ class DynProvider(BaseProvider):
return [{ return [{
'txtdata': v, 'txtdata': v,
'ttl': record.ttl, 'ttl': record.ttl,
} for v in record.values]
} for v in record.chunked_values]
def _kwargs_for_SRV(self, record): def _kwargs_for_SRV(self, record):
return [{ return [{


+ 1
- 2
octodns/provider/route53.py View File

@ -114,8 +114,7 @@ class _Route53Record(object):
for v in record.values] for v in record.values]
def _values_for_quoted(self, record): def _values_for_quoted(self, record):
return ['"{}"'.format(v.replace('"', '\\"'))
for v in record.values]
return record.chunked_values
_values_for_SPF = _values_for_quoted _values_for_SPF = _values_for_quoted
_values_for_TXT = _values_for_quoted _values_for_TXT = _values_for_quoted


+ 23
- 12
octodns/record.py View File

@ -704,8 +704,8 @@ class SshfpRecord(_ValuesMixin, Record):
_unescaped_semicolon_re = re.compile(r'\w;') _unescaped_semicolon_re = re.compile(r'\w;')
class SpfRecord(_ValuesMixin, Record):
_type = 'SPF'
class _ChunkedValuesMixin(_ValuesMixin):
CHUNK_SIZE = 255
@classmethod @classmethod
def _validate_value(cls, value): def _validate_value(cls, value):
@ -714,9 +714,29 @@ class SpfRecord(_ValuesMixin, Record):
return [] return []
def _process_values(self, values): def _process_values(self, values):
ret = []
for v in values:
if v and v[0] == '"':
v = v[1:-1]
ret.append(v.replace('" "', ''))
return ret
@property
def chunked_values(self):
values = []
for v in self.values:
v = v.replace('"', '\\"')
vs = [v[i:i + self.CHUNK_SIZE]
for i in range(0, len(v), self.CHUNK_SIZE)]
vs = '" "'.join(vs)
values.append('"{}"'.format(vs))
return values return values
class SpfRecord(_ChunkedValuesMixin, Record):
_type = 'SPF'
class SrvValue(object): class SrvValue(object):
@classmethod @classmethod
@ -797,14 +817,5 @@ class SrvRecord(_ValuesMixin, Record):
return [SrvValue(v) for v in values] return [SrvValue(v) for v in values]
class TxtRecord(_ValuesMixin, Record):
class TxtRecord(_ChunkedValuesMixin, Record):
_type = 'TXT' _type = 'TXT'
@classmethod
def _validate_value(cls, value):
if _unescaped_semicolon_re.search(value):
return ['unescaped ;']
return []
def _process_values(self, values):
return values

+ 56
- 0
tests/test_octodns_record.py View File

@ -1490,3 +1490,59 @@ class TestRecordValidation(TestCase):
'value': 'this has some; semi-colons\; in it', 'value': 'this has some; semi-colons\; in it',
}) })
self.assertEquals(['unescaped ;'], ctx.exception.reasons) self.assertEquals(['unescaped ;'], ctx.exception.reasons)
def test_TXT_long_value_chunking(self):
expected = '"Lorem ipsum dolor sit amet, consectetur adipiscing ' \
'elit, seddo eiusmod tempor incididunt ut labore et dolore ' \
'magna aliqua. Ut enim ad minim veniam, quis nostrud ' \
'exercitation ullamco laboris nisi ut aliquip ex ea commodo ' \
'consequat. Duis aute irure dolor in" " reprehenderit in ' \
'voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' \
'Excepteur sint occaecat cupidatat non proident, sunt in culpa ' \
'qui officia deserunt mollit anim id est laborum."'
# Single string
single = Record.new(self.zone, '', {
'type': 'TXT',
'ttl': 600,
'values': [
'hello world',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed'
'do eiusmod tempor incididunt ut labore et dolore magna '
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '
'ullamco laboris nisi ut aliquip ex ea commodo consequat. '
'Duis aute irure dolor in reprehenderit in voluptate velit '
'esse cillum dolore eu fugiat nulla pariatur. Excepteur sint '
'occaecat cupidatat non proident, sunt in culpa qui officia '
'deserunt mollit anim id est laborum.',
'this has some\; semi-colons\; in it',
]
})
self.assertEquals(3, len(single.values))
self.assertEquals(3, len(single.chunked_values))
# Note we are checking that this normalizes the chunking, not that we
# get out what we put in.
self.assertEquals(expected, single.chunked_values[0])
# Chunked
chunked = Record.new(self.zone, '', {
'type': 'TXT',
'ttl': 600,
'values': [
'"hello world"',
'"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed'
'do eiusmod tempor incididunt ut labore et dolore magna '
'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '
'ullamco laboris nisi ut aliquip ex" " ea commodo consequat. '
'Duis aute irure dolor in reprehenderit in voluptate velit '
'esse cillum dolore eu fugiat nulla pariatur. Excepteur sint '
'occaecat cupidatat non proident, sunt in culpa qui officia '
'deserunt mollit anim id est laborum."',
'"this has some\; semi-colons\; in it"',
]
})
self.assertEquals(expected, chunked.chunked_values[0])
# should be single values, no quoting
self.assertEquals(single.values, chunked.values)
# should be chunked values, with quoting
self.assertEquals(single.chunked_values, chunked.chunked_values)

Loading…
Cancel
Save