Browse Source

Validate Record name & fqdn length

pull/415/head
Ross McFarland 6 years ago
parent
commit
9e948aa4c8
No known key found for this signature in database GPG Key ID: 61C10C4FC8FE4A89
2 changed files with 51 additions and 14 deletions
  1. +23
    -14
      octodns/record/__init__.py
  2. +28
    -0
      tests/test_octodns_record.py

+ 23
- 14
octodns/record/__init__.py View File

@ -82,6 +82,7 @@ class Record(EqualityTupleMixin):
@classmethod @classmethod
def new(cls, zone, name, data, source=None, lenient=False): def new(cls, zone, name, data, source=None, lenient=False):
name = text_type(name)
fqdn = '{}.{}'.format(name, zone.name) if name else zone.name fqdn = '{}.{}'.format(name, zone.name) if name else zone.name
try: try:
_type = data['type'] _type = data['type']
@ -105,7 +106,7 @@ class Record(EqualityTupleMixin):
}[_type] }[_type]
except KeyError: except KeyError:
raise Exception('Unknown record type: "{}"'.format(_type)) raise Exception('Unknown record type: "{}"'.format(_type))
reasons = _class.validate(name, data)
reasons = _class.validate(name, fqdn, data)
try: try:
lenient |= data['octodns']['lenient'] lenient |= data['octodns']['lenient']
except KeyError: except KeyError:
@ -118,8 +119,16 @@ class Record(EqualityTupleMixin):
return _class(zone, name, data, source=source) return _class(zone, name, data, source=source)
@classmethod @classmethod
def validate(cls, name, data):
def validate(cls, name, fqdn, data):
reasons = [] reasons = []
n = len(fqdn)
if n > 253:
reasons.append('invalid fqdn, "{}" is too long at {} chars, max '
'is 253'.format(fqdn, n))
n = len(name)
if n > 63:
reasons.append('invalid name, "{}" is too long at {} chars, max '
'is 63'.format(name, n))
try: try:
ttl = int(data['ttl']) ttl = int(data['ttl'])
if ttl < 0: if ttl < 0:
@ -258,8 +267,8 @@ class GeoValue(EqualityTupleMixin):
class _ValuesMixin(object): class _ValuesMixin(object):
@classmethod @classmethod
def validate(cls, name, data):
reasons = super(_ValuesMixin, cls).validate(name, data)
def validate(cls, name, fqdn, data):
reasons = super(_ValuesMixin, cls).validate(name, fqdn, data)
values = data.get('values', data.get('value', [])) values = data.get('values', data.get('value', []))
@ -311,8 +320,8 @@ class _GeoMixin(_ValuesMixin):
''' '''
@classmethod @classmethod
def validate(cls, name, data):
reasons = super(_GeoMixin, cls).validate(name, data)
def validate(cls, name, fqdn, data):
reasons = super(_GeoMixin, cls).validate(name, fqdn, data)
try: try:
geo = dict(data['geo']) geo = dict(data['geo'])
for code, values in geo.items(): for code, values in geo.items():
@ -358,8 +367,8 @@ class _GeoMixin(_ValuesMixin):
class _ValueMixin(object): class _ValueMixin(object):
@classmethod @classmethod
def validate(cls, name, data):
reasons = super(_ValueMixin, cls).validate(name, data)
def validate(cls, name, fqdn, data):
reasons = super(_ValueMixin, cls).validate(name, fqdn, data)
reasons.extend(cls._value_type.validate(data.get('value', None), reasons.extend(cls._value_type.validate(data.get('value', None),
cls._type)) cls._type))
return reasons return reasons
@ -485,8 +494,8 @@ class _DynamicMixin(object):
r'(-(?P<subdivision_code>\w\w))?)?$') r'(-(?P<subdivision_code>\w\w))?)?$')
@classmethod @classmethod
def validate(cls, name, data):
reasons = super(_DynamicMixin, cls).validate(name, data)
def validate(cls, name, fqdn, data):
reasons = super(_DynamicMixin, cls).validate(name, fqdn, data)
if 'dynamic' not in data: if 'dynamic' not in data:
return reasons return reasons
@ -803,11 +812,11 @@ class CnameRecord(_DynamicMixin, _ValueMixin, Record):
_value_type = CnameValue _value_type = CnameValue
@classmethod @classmethod
def validate(cls, name, data):
def validate(cls, name, fqdn, data):
reasons = [] reasons = []
if name == '': if name == '':
reasons.append('root CNAME not allowed') reasons.append('root CNAME not allowed')
reasons.extend(super(CnameRecord, cls).validate(name, data))
reasons.extend(super(CnameRecord, cls).validate(name, fqdn, data))
return reasons return reasons
@ -1181,11 +1190,11 @@ class SrvRecord(_ValuesMixin, Record):
_name_re = re.compile(r'^_[^\.]+\.[^\.]+') _name_re = re.compile(r'^_[^\.]+\.[^\.]+')
@classmethod @classmethod
def validate(cls, name, data):
def validate(cls, name, fqdn, data):
reasons = [] reasons = []
if not cls._name_re.match(name): if not cls._name_re.match(name):
reasons.append('invalid name') reasons.append('invalid name')
reasons.extend(super(SrvRecord, cls).validate(name, data))
reasons.extend(super(SrvRecord, cls).validate(name, fqdn, data))
return reasons return reasons


+ 28
- 0
tests/test_octodns_record.py View File

@ -1217,6 +1217,34 @@ class TestRecordValidation(TestCase):
zone = Zone('unit.tests.', []) zone = Zone('unit.tests.', [])
def test_base(self): def test_base(self):
# fqdn length, DNS defins max as 253
with self.assertRaises(ValidationError) as ctx:
# The . will put this over the edge
name = 'x' * (253 - len(self.zone.name))
Record.new(self.zone, name, {
'ttl': 300,
'type': 'A',
'value': '1.2.3.4',
})
reason = ctx.exception.reasons[0]
self.assertTrue(reason.startswith('invalid fqdn, "xxxx'))
self.assertTrue(reason.endswith('.unit.tests." is too long at 254'
' chars, max is 253'))
# label length, DNS defins max as 63
with self.assertRaises(ValidationError) as ctx:
# The . will put this over the edge
name = 'x' * 64
Record.new(self.zone, name, {
'ttl': 300,
'type': 'A',
'value': '1.2.3.4',
})
reason = ctx.exception.reasons[0]
self.assertTrue(reason.startswith('invalid name, "xxxx'))
self.assertTrue(reason.endswith('xxx" is too long at 64'
' chars, max is 63'))
# no ttl # no ttl
with self.assertRaises(ValidationError) as ctx: with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', { Record.new(self.zone, '', {


Loading…
Cancel
Save