From 5a9f9497ee379d1f58cf521d623fb9ebbc23ce17 Mon Sep 17 00:00:00 2001 From: Bob Halley Date: Sun, 13 Jun 2021 14:50:13 -0700 Subject: [PATCH] Add want_origin parameter to zone to_text() and to_file(). [Issue #654] --- dns/zone.py | 25 ++++++- tests/example4.good | 154 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_zone.py | 28 ++++++++ 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 tests/example4.good diff --git a/dns/zone.py b/dns/zone.py index 65eb35b2..9c3204b3 100644 --- a/dns/zone.py +++ b/dns/zone.py @@ -584,7 +584,7 @@ class Zone(dns.transaction.TransactionManager): yield (name, rds.ttl, rdata) def to_file(self, f, sorted=True, relativize=True, nl=None, - want_comments=False): + want_comments=False, want_origin=False): """Write a zone to a file. *f*, a file or `str`. If *f* is a string, it is treated @@ -606,6 +606,10 @@ class Zone(dns.transaction.TransactionManager): *want_comments*, a ``bool``. If ``True``, emit end-of-line comments as part of writing the file. If ``False``, the default, do not emit them. + + *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at + the start of the file. If ``False``, the default, do not emit + one. """ with contextlib.ExitStack() as stack: @@ -628,6 +632,16 @@ class Zone(dns.transaction.TransactionManager): nl_b = nl nl = nl.decode() + if want_origin: + l = '$ORIGIN ' + self.origin.to_text() + l_b = l.encode(file_enc) + try: + f.write(l_b) + f.write(nl_b) + except TypeError: # textual mode + f.write(l) + f.write(nl) + if sorted: names = list(self.keys()) names.sort() @@ -647,7 +661,7 @@ class Zone(dns.transaction.TransactionManager): f.write(nl) def to_text(self, sorted=True, relativize=True, nl=None, - want_comments=False): + want_comments=False, want_origin=False): """Return a zone's text as though it were written to a file. *sorted*, a ``bool``. If True, the default, then the file @@ -667,10 +681,15 @@ class Zone(dns.transaction.TransactionManager): as part of writing the file. If ``False``, the default, do not emit them. + *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at + the start of the output. If ``False``, the default, do not emit + one. + Returns a ``str``. """ temp_buffer = io.StringIO() - self.to_file(temp_buffer, sorted, relativize, nl, want_comments) + self.to_file(temp_buffer, sorted, relativize, nl, want_comments, + want_origin) return_value = temp_buffer.getvalue() temp_buffer.close() return return_value diff --git a/tests/example4.good b/tests/example4.good new file mode 100644 index 00000000..f2c90d70 --- /dev/null +++ b/tests/example4.good @@ -0,0 +1,154 @@ +$ORIGIN example. +@ 300 IN SOA ns1 hostmaster 1 2 3 4 5 +@ 300 IN NS ns1 +@ 300 IN NS ns2 +@ 300 IN NSEC3PARAM 1 1 12 aabbccdd +@ 300 IN NSEC3PARAM 1 1 12 - +* 300 IN MX 10 mail +a 300 IN TXT "foo foo foo" +a 300 IN PTR foo.net. +a01 3600 IN A 0.0.0.0 +a02 3600 IN A 255.255.255.255 +aaaa01 3600 IN AAAA ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +aaaa02 3600 IN AAAA ::1 +afsdb01 3600 IN AFSDB 0 hostname +afsdb02 3600 IN AFSDB 65535 . +amtrelay01 3600 IN AMTRELAY 0 0 0 . +amtrelay02 3600 IN AMTRELAY 0 1 0 . +amtrelay03 3600 IN AMTRELAY 10 0 1 203.0.113.15 +amtrelay04 3600 IN AMTRELAY 10 0 2 2001:db8::15 +amtrelay05 3600 IN AMTRELAY 128 1 3 amtrelays.example.com. +apl01 3600 IN APL 1:192.168.32.0/21 !1:192.168.38.0/28 +apl02 3600 IN APL 1:224.0.0.0/4 2:FF00:0:0:0:0:0:0:0/8 +avc01 3600 IN AVC "app-name:WOLFGANG|app-class:OAM|business=yes" +b 300 IN CNAME foo.net. +c 300 IN A 73.80.65.49 +caa01 3600 IN CAA 0 issue "ca.example.net" +caa02 3600 IN CAA 0 iodef "mailto:security@example.com" +caa03 3600 IN CAA 0 iodef "http://iodef.example.com/" +caa04 3600 IN CAA 0 issue "ca.example.net; account=230123" +caa05 3600 IN CAA 0 issue "ca.example.net; policy=ev" +caa06 3600 IN CAA 128 tbs "Unknown" +cdnskey01 3600 IN CDNSKEY 256 3 8 AwEAAbmiLgh411Pz3v3XCSBrvYf52A/G v55ItN1NbOLHCqt3Ec3p+VB/kQ87VjjM rycanZFnZT4l9uCFuYh21CcyxVpcxExb M0UuhX5rJoDyeFSXoQlkHrB01osPl5Vr i5YmKtcmqGxZ9An0VSunohkyiX1SrNRZ SdQnk9/pIHDe/c8D +cds01 3600 IN CDS 12345 3 1 123456789abcdef67890123456789abcdef67890 +cert01 3600 IN CERT 65534 65535 PRIVATEOID MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +cname01 3600 IN CNAME cname-target. +cname02 3600 IN CNAME cname-target +cname03 3600 IN CNAME . +csync0 3600 IN CSYNC 12345 0 A MX RRSIG NSEC TYPE1234 +d 300 IN A 73.80.65.49 +dhcid01 3600 IN DHCID AAIBY2/AuCccgoJbsaxcQc9TUapptP69 lOjxfNuVAA2kjEA= +dhcid02 3600 IN DHCID AAEBOSD+XR3Os/0LozeXVqcNc7FwCfQd WL3b/NaiUDlW2No= +dhcid03 3600 IN DHCID AAABxLmlskllE0MVjd57zHcWmEH3pCQ6 VytcKD//7es/deY= +dlv01 3600 IN DLV 12345 3 1 123456789abcdef67890123456789abcdef67890 +dname01 3600 IN DNAME dname-target. +dname02 3600 IN DNAME dname-target +dname03 3600 IN DNAME . +dnskey01 3600 IN DNSKEY 512 255 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +dnskey02 3600 IN DNSKEY 257 3 1 AQMFD5raczCJHViKtLYhWGz8hMY9UGRu niJDBzC7w0aRyzWZriO6i2odGWWQVucZ qKVsENW91IOW4vqudngPZsY3GvQ/xVA8 /7pyFj6b7Esga60zyGW6LFe9r8n6paHr lG5ojqf0BaqHT+8= +ds01 3600 IN DS 12345 3 1 123456789abcdef67890123456789abcdef67890 +e 300 IN MX 10 mail +e 300 IN TXT "one" +e 300 IN TXT "three" +e 300 IN TXT "two" +e 300 IN A 73.80.65.49 +e 300 IN A 73.80.65.50 +e 300 IN A 73.80.65.52 +e 300 IN A 73.80.65.51 +f 300 IN A 73.80.65.52 +gpos01 3600 IN GPOS -22.6882 116.8652 250.0 +hinfo01 3600 IN HINFO "Generic PC clone" "NetBSD-1.4" +hinfo02 3600 IN HINFO "PC" "NetBSD" +hip01 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D +hip02 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs.example.com. +hip03 3600 IN HIP 2 200100107b1a74df365639cc39f1d578 AwEAAbdxyhNuSutc5EMzxTs9LBPCIkOFH8cIvM4p9+LrV4e19WzK00+CI6zBCQTdtWsuxKbWIy87UOoJTwkUs7lBu+Upr1gsNrut79ryra+bSRGQb1slImA8YVJyuIDsj7kwzG7jnERNqnWxZ48AWkskmdHaVDP4BcelrTI3rMXdXF5D rvs1.example.com. rvs2.example.com. +https01 3600 IN HTTPS 0 svc +https02 3600 IN HTTPS 1 . port="8002" ech="abcd" +ipseckey01 3600 IN IPSECKEY 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey02 3600 IN IPSECKEY 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey03 3600 IN IPSECKEY 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey04 3600 IN IPSECKEY 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +ipseckey05 3600 IN IPSECKEY 10 3 2 mygateway2 AQNRU3mG7TVTO2BkR47usntb102uFJtu gbo6BSGvgqt4AQ== +isdn01 3600 IN ISDN "isdn-address" +isdn02 3600 IN ISDN "isdn-address" "subaddress" +isdn03 3600 IN ISDN "isdn-address" +isdn04 3600 IN ISDN "isdn-address" "subaddress" +kx01 3600 IN KX 10 kdc +kx02 3600 IN KX 10 . +loc01 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc02 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 20.00m 2000.00m 20.00m +loc03 3600 IN LOC 60 9 0.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc04 3600 IN LOC 60 9 1.500 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc05 3600 IN LOC 60 9 1.510 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc06 3600 IN LOC 60 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc07 3600 IN LOC 0 9 1.000 N 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +loc08 3600 IN LOC 0 9 1.000 S 24 39 0.000 E 10.00m 90000000.00m 2000.00m 20.00m +mx01 3600 IN MX 10 mail +mx02 3600 IN MX 10 . +naptr01 3600 IN NAPTR 0 0 "" "" "" . +naptr02 3600 IN NAPTR 65535 65535 "blurgh" "blorf" "blegh" foo. +ns1 300 IN A 10.53.0.1 +ns2 300 IN A 10.53.0.2 +nsap-ptr01 3600 IN NSAP-PTR foo. +nsap-ptr01 3600 IN NSAP-PTR . +nsap01 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsap02 3600 IN NSAP 0x47000580005a0000000001e133ffffff00016100 +nsec01 3600 IN NSEC a.secure A MX RRSIG NSEC TYPE1234 +nsec02 3600 IN NSEC . NSAP-PTR NSEC +nsec03 3600 IN NSEC . NSEC TYPE65535 +nsec301 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +nsec302 3600 IN NSEC3 1 1 12 - 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM +openpgpkey 3600 IN OPENPGPKEY mQENBEteQDsBCADYnatn9+5t43AdJlVk9dZC2RM0idPQcmrrKcjeAWDnISqoJzkvQ8ifX6mefquTBsDZC279uXShyTffYzQtvP2r9ewkK7zmSv52Ar563TSULAMwiLpe0gGQE0ex20mX5ggtYn6czdbEtcKpW0t+AfDqRk5YcpgqfZKXapKQ+A3CwWJKP9i3ldx2Jz//kuru4YqROLBYyB8D6V2jNUFOdaP6j5C5prh9dxfYFp2O/xFeAKLWlWuH9o96INUoIhgdEyj9PHPT3c821NMZu8tCvsZgUB+QPbHA/QYGa+aollcdGkJpVxXoHhbu6aMx/B+pXg55WM5pqOxmoVjyViHIUYfPABEBAAG0IUJvYiBIYWxsZXkgPGhhbGxleUBkbnNweXRob24ub3JnPokBPgQTAQIAKAUCS15AOwIbAwUJA8JnAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6o6Gb8yUnXaflQgAhlhIqZGncRw3LV3d24JmPD+UEcEGiVh2b/Ic/1TMec46Ts7ZqRXAcOATNteQmpzqexx+BRKDWU8ZgYx12J4GZmC06jABr2JDWxgvbMX9qjkUUgDGZZgAS/B2x5AmKgy2ZnCUlaKfePcKmtKTB9yNJ8v/WERlFdGaUveEUiFU8g75xp1Hj9Wp9sXCg9yeG1K2RwQ3RQd5tLudhyE67EQdFGgqQFynR53md7cmVhAGopKLwMkpCtToKUlxxlfnDfpKZhhXThmhA0PsUQUkJptfGwYwH3O2N3KzfUw3wXRvLa3hona3TlHk3kfg7Qyd7oP4AZGbJKp97YHnfqo1kp8rObkBDQRLXkA7AQgA0ePG7g5GgZ/1SdtGZlJJiE2X15vTUc3KGfmx/kI5NaUDu4fXb+XK+yFy9I/X+UJ46JSkyhj6QvUxpoI+A7WWk9ThfjbynoZxRD820KbqidqxBSgtFF36SRWzmX8DZfKKAskT9ZGU1odeSKDXLCJF7qAbZVRTuFRiDFGwtoVIICeE6Xd65JO6ufhad+ELhgFt95vRwTiFvVrBRjwF7ZgN/nOXfYncxZ/2mpFqfwsnB2eu0A2XZBm8IngsSmr/Wrz1RQ7+SNMqt77E7CKwBX7UIAZgyoJxIRxWirJoOt1rIm5VUqRR25ubXLuzx9PaHYiC5GiQIU45pWAd0IWcTI/MJQARAQABiQElBBgBAgAPBQJLXkA7AhsMBQkDwmcAAAoJEOqOhm/MlJ12HRsIAKrB9E++9X9W6VTXBfdkShCFv0ykZVn2eVs6tkqzoub9s4f+Z5ylWw+a5nkMDMdGVe6bn4A3oIAbf0Tjykq1AetZLVPsHl/QosTbSQluis/PEvJkTQXHaKHB3bFhwA90c/3HNhrLGugt9AmcfLf9LAynXDgNLV5eYdPYqfKE+27qjEBARf6PYh/8WQ8CPKS8DILFbwCZbRxUogyrZf/7AiHAGdJi8dmpR1WPQYef2hF3kqGX6NngLBPzZ6CQRaHBhD4pHU1S/IRSlx9/3Ytww32PYD9AyO732NmCUcq3bmvqcOWy4Cc1NkEwU0Vg0qzwVBNGb84v/ex2MouwtAYScwc= +ptr01 3600 IN PTR @ +px01 3600 IN PX 65535 foo. bar. +px02 3600 IN PX 65535 . . +rp01 3600 IN RP mbox-dname txt-dname +rp02 3600 IN RP . . +rrsig01 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +rrsig02 3600 IN RRSIG NSEC 1 3 3600 20200101000000 20030101000000 2143 foo MxFcby9k/yvedMfQgKzhH5er0Mu/vILz 45IkskceFGgiWCn/GxHhai6VAuHAoNUz 4YoU1tVfSCSqQYn6//11U6Nld80jEeC8 aTrO+KKmCaY= +rt01 3600 IN RT 0 intermediate-host +rt02 3600 IN RT 65535 . +s 300 IN NS ns.s +ns.s 300 IN A 73.80.65.49 +smimea1 3600 IN SMIMEA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 +smimea2 3600 IN SMIMEA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 +smimea3 3600 IN SMIMEA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 +spf 3600 IN SPF "v=spf1 mx -all" +srv01 3600 IN SRV 0 0 0 . +srv02 3600 IN SRV 65535 65535 65535 old-slow-box.example.com. +sshfp1 3600 IN SSHFP 1 1 aa549bfe898489c02d1715d97d79c57ba2fa76ab +svcb01 3600 IN SVCB 100 foo.com. mandatory="alpn,port" alpn="h2,h3" no-default-alpn port="12345" ipv4hint="1.2.3.4,4.3.2.1" ech="abcd" ipv6hint="1::2,3::4" key12345="foo" +t 301 IN A 73.80.65.49 +tlsa1 3600 IN TLSA 3 1 1 a9cdf989b504fe5dca90c0d2167b6550570734f7c763e09fdf88904e06157065 +tlsa2 3600 IN TLSA 1 0 1 efddf0d915c7bdc5782c0881e1b2a95ad099fbdd06d7b1f77982d9364338d955 +tlsa3 3600 IN TLSA 1 0 2 81ee7f6c0ecc6b09b7785a9418f54432de630dd54dc6ee9e3c49de547708d236d4c413c3e97e44f969e635958aa410495844127c04883503e5b024cf7a8f6a94 +txt01 3600 IN TXT "foo" +txt02 3600 IN TXT "foo" "bar" +txt03 3600 IN TXT "foo" +txt04 3600 IN TXT "foo" "bar" +txt05 3600 IN TXT "foo bar" +txt06 3600 IN TXT "foo bar" +txt07 3600 IN TXT "foo bar" +txt08 3600 IN TXT "foo\010bar" +txt09 3600 IN TXT "foo\010bar" +txt10 3600 IN TXT "foo bar" +txt11 3600 IN TXT "\"foo\"" +txt12 3600 IN TXT "\"foo\"" +txt13 3600 IN TXT "foo;" +txt14 3600 IN TXT "foo;" +txt15 3600 IN TXT "bar\\;" +u 300 IN TXT "txt-not-in-nxt" +a.u 300 IN A 73.80.65.49 +b.u 300 IN A 73.80.65.49 +unknown2 3600 IN TYPE999 \# 8 0a0000010a000001 +unknown3 3600 IN A 127.0.0.2 +uri01 3600 IN URI 10 1 "ftp://ftp1.example.com/public" +uri02 3600 IN URI 10 1 "http://www.example.com/path" +wks01 3600 IN WKS 10.0.0.1 6 0 1 2 21 23 +wks02 3600 IN WKS 10.0.0.1 17 0 1 2 53 +wks03 3600 IN WKS 10.0.0.2 6 65535 +x2501 3600 IN X25 "123456789" +zonemd01 3600 IN ZONEMD 2018031900 1 1 62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306 +zonemd02 3600 IN ZONEMD 2018031900 1 2 08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27 +zonemd03 3600 IN ZONEMD 2018031900 1 240 e2d523f654b9422a96c5a8f44607bbee +zonemd04 3600 IN ZONEMD 2018031900 241 1 e1846540e33a9e4189792d18d5d131f605fc283eaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/tests/test_zone.py b/tests/test_zone.py index 26adc878..e8dd9667 100644 --- a/tests/test_zone.py +++ b/tests/test_zone.py @@ -332,6 +332,34 @@ class ZoneTestCase(unittest.TestCase): os.unlink(here('example3.out')) self.assertTrue(ok) + def testToFileTextualWithOrigin(self): + z = dns.zone.from_file(here('example'), 'example') + try: + f = open(here('example4-textual.out'), 'w') + z.to_file(f, want_origin=True) + f.close() + ok = compare_files('testToFileTextualWithOrigin', + here('example4-textual.out'), + here('example4.good')) + finally: + if not _keep_output: + os.unlink(here('example4-textual.out')) + self.assertTrue(ok) + + def testToFileBinaryWithOrigin(self): + z = dns.zone.from_file(here('example'), 'example') + try: + f = open(here('example4-binary.out'), 'wb') + z.to_file(f, want_origin=True) + f.close() + ok = compare_files('testToFileBinaryWithOrigin', + here('example4-binary.out'), + here('example4.good')) + finally: + if not _keep_output: + os.unlink(here('example4-binary.out')) + self.assertTrue(ok) + def testFromText(self): z = dns.zone.from_text(example_text, 'example.', relativize=True) f = StringIO() -- 2.47.3