From b139d266c9a2ec1c236c4962bdb5b70029039b80 Mon Sep 17 00:00:00 2001 From: Aquifoliales <103569748+Aquifoliales@users.noreply.github.com> Date: Wed, 4 May 2022 11:01:50 +0200 Subject: [PATCH] Support for TLSA record, https://www.rfc-editor.org/rfc/rfc6698.txt --- docs/records.md | 1 + octodns/record/__init__.py | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/docs/records.md b/docs/records.md index 182a109..30d689a 100644 --- a/docs/records.md +++ b/docs/records.md @@ -18,6 +18,7 @@ OctoDNS supports the following record types: * `SPF` * `SRV` * `SSHFP` +* `TLSA` * `TXT` * `URLFWD` diff --git a/octodns/record/__init__.py b/octodns/record/__init__.py index 6fe3f90..ce73752 100644 --- a/octodns/record/__init__.py +++ b/octodns/record/__init__.py @@ -1517,6 +1517,71 @@ class SrvRecord(ValuesMixin, Record): Record.register_type(SrvRecord) +class TlsaValue(EqualityTupleMixin): + + @classmethod + def validate(cls, data, _type): + if not isinstance(data, (list, tuple)): + data = (data,) + reasons = [] + for value in data: + try: + certificate_usage = int(value.get('certificate_usage', 0)) + if certificate_usage < 0 or certificate_usage > 3: + reasons.append(f'invalid certificate_usage "{certificate_usage}"') + except ValueError: + reasons.append(f'invalid certificate_usage "{value["certificate_usage"]}"') + + try: + selector = int(value.get('selector', 0)) + if selector < 0 or selector > 1: + reasons.append(f'invalid selector "{selector}"') + except ValueError: + reasons.append(f'invalid selector "{value["selector"]}"') + + try: + matching_type = int(value.get('matching_type', 0)) + if matching_type < 0 or matching_type > 2: + reasons.append(f'invalid matching_type "{matching_type}"') + except ValueError: + reasons.append(f'invalid matching_type "{value["matching_type"]}"') + + if 'certificate_association_data' not in value: + reasons.append('missing certificate_association_data') + return reasons + + @classmethod + def process(cls, values): + return [TlsaValue(v) for v in values] + + def __init__(self, value): + self.certificate_usage = int(value.get('certificate_usage', 0)) + self.selector = int(value.get('selector', 0)) + self.matching_type = int(value.get('matching_type', 0)) + self.certificate_association_data = value['certificate_association_data'] + + @property + def data(self): + return { + 'certificate_usage': self.certificate_usage, + 'selector': self.selector, + 'matching_type': self.matching_type, + 'certificate_association_data': self.certificate_association_data, + } + + def _equality_tuple(self): + return (self.certificate_usage, self.selector, self.matching_type, self.certificate_association_data) + + def __repr__(self): + return f'{self.certificate_usage} {self.selector} {self.matching_type}"{self.certificate_association_data}"' + + +class TlsaRecord(ValuesMixin, Record): + _type = 'TLSA' + _value_type = TlsaValue + +Record.register_type(TlsaRecord) + class _TxtValue(_ChunkedValue): pass