add support for CAA records
[zonemaker.git] / zone.py
diff --git a/zone.py b/zone.py
index 5cd0d72cac2b744fd4b807cf49e79b4ffd0434e9..d325915074b943a9ee11aeaddf1aa3fe0417e163 100644 (file)
--- a/zone.py
+++ b/zone.py
@@ -33,7 +33,7 @@ week = 7*day
 
 REGEX_label = r'[a-zA-Z90-9]([a-zA-Z90-9-]{0,61}[a-zA-Z90-9])?' # max. 63 characters; must not start or end with hyphen
 REGEX_ipv4  = r'^\d{1,3}(\.\d{1,3}){3}$'
-REGEX_ipv6  = r'^[a-fA-F0-9]{1,4}(:[a-fA-F0-9]{1,4}){7}$'
+REGEX_ipv6  = r'^[a-fA-F0-9]{1,4}(::?[a-fA-F0-9]{1,4}){1,7}$'
 
 def check_label(label: str) -> str:
     label = str(label)
@@ -110,6 +110,11 @@ def concatenate(root, path):
         return path
     return path+"."+root
 
+def escape_TXT(text):
+    for c in ('\\', '\"'):
+        text = text.replace(c, '\\'+c)
+    return text
+
 
 ## Enums
 class Protocol:
@@ -182,13 +187,17 @@ class TXT:
         for c in ('\n', '\r', '\t'):
             if c in text:
                 raise Exception("TXT record {0} contains invalid character")
-        # escape text
-        for c in ('\\', '\"'):
-            text = text.replace(c, '\\'+c)
         self._text = text
     
     def generate_rr(self):
-        return RR('@', 'TXT', '"{0}"'.format(self._text))
+        text = escape_TXT(self._text)
+        # split into chunks of max. 255 characters; be careful not to split right after a backslash
+        chunks = re.findall(r'.{0,254}[^\\]', text)
+        assert sum(len(c) for c in chunks) == len (text)
+        chunksep = '"\n' + ' '*20 + '"'
+        chunked = '( "' + chunksep.join(chunks) + '" )'
+        # generate the chunks
+        return RR('@', 'TXT', chunked)
 
 
 class DKIM(TXT): # helper class to treat DKIM more antively
@@ -250,6 +259,19 @@ class TLSA:
     def generate_rr(self):
         return RR('_{}._{}'.format(self._port, self._protocol), 'TLSA', '{} {} {} {}'.format(self._usage, self._selector, self._matching_type, self._data))
 
+class CAA:
+    class Tag:
+        Issue = "issue"
+        IssueWild = "issuewild"
+
+    def __init__(self, flag: int, tag: str, value: str) -> None:
+        self._flag = int(flag)
+        self._tag = str(tag)
+        self._value = str(value)
+
+    def generate_rr(self):
+        return RR('@', 'CAA', '{} {} {}'.format(self._flag, self._tag, self._value))
+
 
 class CNAME:
     def __init__(self, name: str) -> None:
@@ -296,12 +318,12 @@ def CName(name: str) -> Name:
     return Name(CNAME(name))
 
 
-def Delegation(name: str) -> Name:
-    return Name(NS(name))
+def Delegation(*names) -> Name:
+    return Name(list(map(NS, names)))
 
 
-def SecureDelegation(name: str, tag: int, alg: int, digest: int, key: str) -> Name:
-    return Name(NS(name), DS(tag, alg, digest, key))
+def SecureDelegation(tag: int, alg: int, digest: int, key: str, *names) -> Name:
+    return Name(DS(tag, alg, digest, key), list(map(NS, names)))
 
 
 class Zone: