#
#
#
from fqdn import FQDN
from ..idna import idna_encode
from .validator import ValueValidator
[docs]
def _check_target_trailing_dot(target, _type, key='value'):
if not target:
return []
if target == '.' and _type in ['HTTPS', 'MX', 'SVCB', 'SRV']:
return []
if '{' in target and '}' in target:
return []
target = idna_encode(target)
if not target.endswith('.'):
return [f'{_type} {key} "{target}" missing trailing .']
return []
[docs]
class TargetValueValidator(ValueValidator):
'''
Validates a single-value target FQDN (CNAME, ALIAS, DNAME, PTR).
'''
[docs]
def validate(self, value_cls, data, _type):
return _check_target_format(data, _type)
[docs]
class TargetsValueValidator(ValueValidator):
'''
Validates a list of target FQDNs (NS). Rejects empty lists.
'''
[docs]
def validate(self, value_cls, data, _type):
if not data:
return ['missing value(s)']
reasons = []
for value in data:
reasons += _check_target_format(value, _type)
return reasons
[docs]
class TargetValueBestPracticeValidator(ValueValidator):
'''
Checks that a single-value target ends with a trailing ``.`` (i.e. is
an absolute/fully-qualified name). Without the trailing dot, resolvers
may append the host's search domain, multiplying query traffic.
Enabled as part of the ``best-practice`` validator set::
manager:
enabled:
- best-practice
'''
[docs]
def validate(self, value_cls, data, _type):
return _check_target_trailing_dot(data, _type)
[docs]
class TargetsValueBestPracticeValidator(ValueValidator):
'''
Checks that each target in a multi-value record ends with a trailing
``.`` (i.e. is an absolute/fully-qualified name).
Enabled as part of the ``best-practice`` validator set::
manager:
enabled:
- best-practice
'''
[docs]
def validate(self, value_cls, data, _type):
reasons = []
for value in data:
reasons += _check_target_trailing_dot(value, _type)
return reasons
[docs]
class _TargetValue(str):
VALIDATORS = [
TargetValueValidator('target-value-rfc', sets={'legacy', 'strict'}),
TargetValueBestPracticeValidator(
'target-value-best-practice', sets={'best-practice'}
),
]
[docs]
@classmethod
def parse_rdata_text(self, value):
return value
[docs]
@classmethod
def _schema(cls):
return {'type': 'string'}
[docs]
@classmethod
def process(cls, value):
if value:
return cls(value)
return None
def __new__(cls, v):
v = idna_encode(v)
return super().__new__(cls, v)
@property
def rdata_text(self):
return self
[docs]
def template(self, params):
if '{' not in self:
return self
return self.__class__(self.format(**params))
#
# much like _TargetValue, but geared towards multiple values
[docs]
class _TargetsValue(str):
VALIDATORS = [
TargetsValueValidator('targets-value-rfc', sets={'legacy', 'strict'}),
TargetsValueBestPracticeValidator(
'targets-value-best-practice', sets={'best-practice'}
),
]
[docs]
@classmethod
def parse_rdata_text(cls, value):
return value
[docs]
@classmethod
def _schema(cls):
return {'type': 'string'}
[docs]
@classmethod
def process(cls, values):
return [cls(v) for v in values]
def __new__(cls, v):
v = idna_encode(v)
return super().__new__(cls, v)
@property
def rdata_text(self):
return self
[docs]
def template(self, params):
if '{' not in self:
return self
return self.__class__(self.format(**params))