mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Add support for relative target/exchange/nameserver values
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* Correctly handle FQDNs in TinyDNS config files that end with trailing .'s
|
||||
* Complete rewrite of TinyDnsBaseSource to fully implement the spec and the ipv6
|
||||
extensions
|
||||
* Allow relative target, nameserver, or exchange values
|
||||
|
||||
## v1.0.0.rc0 - 2023-05-16 - First of the ones
|
||||
|
||||
|
||||
+15
-10
@@ -45,16 +45,21 @@ class MxValue(EqualityTupleMixin, dict):
|
||||
reasons.append('missing exchange')
|
||||
continue
|
||||
exchange = idna_encode(exchange)
|
||||
if (
|
||||
exchange != '.'
|
||||
and not FQDN(exchange, allow_underscores=True).is_valid
|
||||
):
|
||||
reasons.append(
|
||||
f'Invalid MX exchange "{exchange}" is not '
|
||||
'a valid FQDN.'
|
||||
)
|
||||
elif not exchange.endswith('.'):
|
||||
reasons.append(f'MX value "{exchange}" missing trailing .')
|
||||
if '.' not in exchange:
|
||||
reasons.append(f'MX exchange "{exchange}" is relative')
|
||||
else:
|
||||
if (
|
||||
exchange != '.'
|
||||
and not FQDN(exchange, allow_underscores=True).is_valid
|
||||
):
|
||||
reasons.append(
|
||||
f'Invalid MX exchange "{exchange}" is not '
|
||||
'a valid FQDN.'
|
||||
)
|
||||
elif not exchange.endswith('.'):
|
||||
reasons.append(
|
||||
f'MX value "{exchange}" missing trailing .'
|
||||
)
|
||||
except KeyError:
|
||||
reasons.append('missing exchange')
|
||||
return reasons
|
||||
|
||||
+14
-9
@@ -69,15 +69,20 @@ class SrvValue(EqualityTupleMixin, dict):
|
||||
reasons.append('missing target')
|
||||
continue
|
||||
target = idna_encode(target)
|
||||
if not target.endswith('.'):
|
||||
reasons.append(f'SRV value "{target}" missing trailing .')
|
||||
if (
|
||||
target != '.'
|
||||
and not FQDN(target, allow_underscores=True).is_valid
|
||||
):
|
||||
reasons.append(
|
||||
f'Invalid SRV target "{target}" is not a valid FQDN.'
|
||||
)
|
||||
if '.' not in target:
|
||||
reasons.append(f'SRV value "{target}" is relative')
|
||||
else:
|
||||
if not target.endswith('.'):
|
||||
reasons.append(
|
||||
f'SRV value "{target}" missing trailing .'
|
||||
)
|
||||
if (
|
||||
target != '.'
|
||||
and not FQDN(target, allow_underscores=True).is_valid
|
||||
):
|
||||
reasons.append(
|
||||
f'Invalid SRV target "{target}" is not a valid FQDN.'
|
||||
)
|
||||
except KeyError:
|
||||
reasons.append('missing target')
|
||||
return reasons
|
||||
|
||||
@@ -19,6 +19,8 @@ class _TargetValue(str):
|
||||
reasons.append('empty value')
|
||||
elif not data:
|
||||
reasons.append('missing value')
|
||||
elif '.' not in data:
|
||||
reasons.append(f'{_type} value "{data}" is relative')
|
||||
else:
|
||||
data = idna_encode(data)
|
||||
if not FQDN(str(data), allow_underscores=True).is_valid:
|
||||
@@ -58,7 +60,9 @@ class _TargetsValue(str):
|
||||
reasons = []
|
||||
for value in data:
|
||||
value = idna_encode(value)
|
||||
if not FQDN(value, allow_underscores=True).is_valid:
|
||||
if '.' not in value:
|
||||
reasons.append(f'{_type} value "{value}" is relative')
|
||||
elif not FQDN(value, allow_underscores=True).is_valid:
|
||||
reasons.append(
|
||||
f'Invalid {_type} value "{value}" is not a valid FQDN.'
|
||||
)
|
||||
|
||||
@@ -262,3 +262,18 @@ class TestRecordMx(TestCase):
|
||||
},
|
||||
)
|
||||
self.assertEqual('.', record.values[0].exchange)
|
||||
|
||||
# relative exchange
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(
|
||||
self.zone,
|
||||
'',
|
||||
{
|
||||
'type': 'MX',
|
||||
'ttl': 600,
|
||||
'value': {'preference': 10, 'value': 'isrelative'},
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
['MX exchange "isrelative" is relative'], ctx.exception.reasons
|
||||
)
|
||||
|
||||
@@ -429,3 +429,23 @@ class TestRecordSrv(TestCase):
|
||||
['Invalid SRV target "100 foo.bar.com." is not a valid FQDN.'],
|
||||
ctx.exception.reasons,
|
||||
)
|
||||
|
||||
# relative target
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(
|
||||
self.zone,
|
||||
'_srv._tcp',
|
||||
{
|
||||
'type': 'SRV',
|
||||
'ttl': 600,
|
||||
'value': {
|
||||
'priority': 1,
|
||||
'weight': 2,
|
||||
'port': 3,
|
||||
'target': 'isrelative',
|
||||
},
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
['SRV value "isrelative" is relative'], ctx.exception.reasons
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record, ValidationError
|
||||
from octodns.record.alias import AliasRecord
|
||||
from octodns.record.target import _TargetValue
|
||||
from octodns.zone import Zone
|
||||
@@ -28,3 +29,32 @@ class TestRecordTarget(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
a = AliasRecord(zone, 'a', {'ttl': 42, 'value': 'some.target.'})
|
||||
self.assertEqual('some.target.', a.value.rdata_text)
|
||||
|
||||
def test_relative_target(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
data = {'ttl': 43, 'type': 'CNAME', 'value': 'isrelative'}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(zone, 'cname', data)
|
||||
self.assertEqual(
|
||||
['CNAME value "isrelative" is relative'], ctx.exception.reasons
|
||||
)
|
||||
cname = Record.new(zone, 'cname', data, lenient=True)
|
||||
self.assertEqual(data['value'], cname.value)
|
||||
|
||||
data = {
|
||||
'ttl': 43,
|
||||
'type': 'NS',
|
||||
'values': ['isrelative1', 'isrelative2'],
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(zone, 'ns', data)
|
||||
self.assertEqual(
|
||||
[
|
||||
'NS value "isrelative1" is relative',
|
||||
'NS value "isrelative2" is relative',
|
||||
],
|
||||
ctx.exception.reasons,
|
||||
)
|
||||
cname = Record.new(zone, 'ns', data, lenient=True)
|
||||
self.assertEqual(data['values'], cname.values)
|
||||
|
||||
Reference in New Issue
Block a user