Browse Source

fix(api): allow underscores in CNAME targets

The previous check was based on the wrong pdns code location.
Consultation on pdns IRC revealed the right one.

Fixes regression from 0605f3879012ce6dc4c06f5d7711875348c9e468
Peter Thomassen 2 years ago
parent
commit
bbcbc55a72
3 changed files with 19 additions and 10 deletions
  1. 17 8
      api/desecapi/dns.py
  2. 1 1
      api/desecapi/tests/test_rrsets.py
  3. 1 1
      test/e2e2/spec/test_api_rr.py

+ 17 - 8
api/desecapi/dns.py

@@ -98,16 +98,23 @@ class LongQuotedTXT(dns.rdtypes.txtbase.TXTBase):
                 file.write(s)
 
 
-def _HostnameMixin(name_field, *, allow_root):
-    # Taken from https://github.com/PowerDNS/pdns/blob/4646277d05f293777a3d2423a3b188ccdf42c6bc/pdns/dnsname.cc#L419
-    hostname_re = re.compile(r"^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\.)+$")
+def _NameMixin(name_field, *, allow_root, hostname=True):
+    pattern = (
+        # https://github.com/PowerDNS/pdns/blob/4646277d05f293777a3d2423a3b188ccdf42c6bc/pdns/dnsname.cc#L419
+        re.compile(r"^(([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)\.)+$")
+        if hostname
+        else
+        # https://github.com/PowerDNS/pdns/blob/4646277d05f293777a3d2423a3b188ccdf42c6bc/pdns/dnsname.cc#L473
+        # with the exception of [/@ :\\]
+        re.compile(r"^(([A-Za-z0-9_*-]+)\.)+$")
+    )
 
     class Mixin:
         def to_text(self, origin=None, relativize=True, **kw):
             name = getattr(self, name_field)
             if (
                 not (allow_root and name == dns.name.root)
-                and hostname_re.match(str(name)) is None
+                and pattern.match(str(name)) is None
             ):
                 raise ValueError(f"invalid {name_field}: {name}")
             return super().to_text(origin, relativize, **kw)
@@ -116,20 +123,22 @@ def _HostnameMixin(name_field, *, allow_root):
 
 
 @dns.immutable.immutable
-class CNAME(_HostnameMixin("target", allow_root=True), dns.rdtypes.ANY.CNAME.CNAME):
+class CNAME(
+    _NameMixin("target", allow_root=True, hostname=False), dns.rdtypes.ANY.CNAME.CNAME
+):
     pass
 
 
 @dns.immutable.immutable
-class MX(_HostnameMixin("exchange", allow_root=True), dns.rdtypes.ANY.MX.MX):
+class MX(_NameMixin("exchange", allow_root=True), dns.rdtypes.ANY.MX.MX):
     pass
 
 
 @dns.immutable.immutable
-class NS(_HostnameMixin("target", allow_root=False), dns.rdtypes.ANY.NS.NS):
+class NS(_NameMixin("target", allow_root=False), dns.rdtypes.ANY.NS.NS):
     pass
 
 
 @dns.immutable.immutable
-class SRV(_HostnameMixin("target", allow_root=True), dns.rdtypes.IN.SRV.SRV):
+class SRV(_NameMixin("target", allow_root=True), dns.rdtypes.IN.SRV.SRV):
     pass

+ 1 - 1
api/desecapi/tests/test_rrsets.py

@@ -840,7 +840,7 @@ class AuthenticatedRRSetTestCase(AuthenticatedRRSetBaseTestCase):
                 "61655 13 4 C838A5C66FCBF83B8B6B50C3CEEC3524777FE4AF8A9FE0172ECAD242 48B0CA1A216DD0D538F20C130DD3059538204B04",
                 "6454 8 5 24396E17E36D031F71C354B06A979A67A01F503E",
             ],
-            "CNAME": ["example.com."],
+            "CNAME": ["example.com.", "*._under-score.-foo_bar.example.net.", "."],
             "CSYNC": ["0 0", "66 1 A", "66 2 AAAA", "66 3 A NS AAAA", "66 15 NSEC"],
             "DHCID": ["aaaaaaaaaaaa", "aa aaa  aaaa a a a"],
             "DLV": [

+ 1 - 1
test/e2e2/spec/test_api_rr.py

@@ -169,7 +169,7 @@ VALID_RECORDS_NON_CANONICAL = {
         '6454 8 2 5C BA665A006F6487625C6218522F09BD3673C25FA10F25CB18459AA1 0DF1F520',
     ],
     'CERT': ['6 0 0 sadfdQ==', '06 00 00 sadfee==', 'IPGP 00 00 sadfee=='],
-    'CNAME': ['EXAMPLE.TEST.'],
+    'CNAME': ['EXAMPLE.TEST.', '*._under-score.-foo_bar.example.net.'],
     'CSYNC': ['066 03  NS  AAAA A'],
     'DHCID': ['aa aaa  aaaa a a a', 'xxxx'],
     'DLV': [