]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Allow built-in `IPv{4,6}Address` objects when constructing A/AAAA rdata (#1183)
authorŠtěpán Balážik <balaziks@users.noreply.github.com>
Mon, 17 Mar 2025 14:58:16 +0000 (15:58 +0100)
committerGitHub <noreply@github.com>
Mon, 17 Mar 2025 14:58:16 +0000 (07:58 -0700)
* Allow built-in IPv{4,6}Address objects when constructing A/AAAA rdata

Also applies elsewhere where dns.rdata.Rdata._as_ipv{4,6}_address is
used.

* Typecheck `tok` argument in dns.rdata.from_text

This now returns a better error message. Previously:

`SyntaxError: 'foo' object has no attribute 'get'`

Now:

`ValueError: tok must be a string or a Tokenizer`

---------

Co-authored-by: Štěpán Balážik <stepan@isc.org>
dns/rdata.py
tests/test_rdata.py

index a305b8fadb444799f1a962a2c886277fb9938f0a..6e5a6f317732c5c3b8e4450f452ace20503f784c 100644 (file)
@@ -22,6 +22,7 @@ import binascii
 import inspect
 import io
 import itertools
+import ipaddress
 import random
 from importlib import import_module
 from typing import Any, Dict, Optional, Tuple, Union
@@ -551,6 +552,8 @@ class Rdata:
             return dns.ipv4.canonicalize(value)
         elif isinstance(value, bytes):
             return dns.ipv4.inet_ntoa(value)
+        elif isinstance(value, ipaddress.IPv4Address):
+            return dns.ipv4.inet_ntoa(value.packed)
         else:
             raise ValueError("not an IPv4 address")
 
@@ -560,6 +563,8 @@ class Rdata:
             return dns.ipv6.canonicalize(value)
         elif isinstance(value, bytes):
             return dns.ipv6.inet_ntoa(value)
+        elif isinstance(value, ipaddress.IPv6Address):
+            return dns.ipv6.inet_ntoa(value.packed)
         else:
             raise ValueError("not an IPv6 address")
 
@@ -752,6 +757,8 @@ def from_text(
     """
     if isinstance(tok, str):
         tok = dns.tokenizer.Tokenizer(tok, idna_codec=idna_codec)
+    if not isinstance(tok, dns.tokenizer.Tokenizer):
+        raise ValueError("tok must be a string or a Tokenizer")
     rdclass = dns.rdataclass.RdataClass.make(rdclass)
     rdtype = dns.rdatatype.RdataType.make(rdtype)
     cls = get_rdata_class(rdclass, rdtype)
index c1d3416c6a09cec75bb35bf2bcd69ec2d8544b3e..8c4c51048894729305a21a9717a40f29b7c41c23 100644 (file)
@@ -17,6 +17,7 @@
 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import io
+import ipaddress
 import operator
 import pickle
 import struct
@@ -29,6 +30,8 @@ import dns.rdataclass
 import dns.rdataset
 import dns.rdatatype
 import dns.rdtypes.ANY.RRSIG
+import dns.rdtypes.IN.A
+import dns.rdtypes.IN.AAAA
 import dns.rdtypes.IN.APL
 import dns.rdtypes.util
 import dns.tokenizer
@@ -896,6 +899,28 @@ class RdataTestCase(unittest.TestCase):
         self.assertEqual(rdata.next_name(), expected_rel)
         self.assertEqual(rdata.next_name(origin), expected_abs)
 
+    def test_a_rdata_made_multiple_ways(self):
+        r1 = dns.rdtypes.IN.A.A(dns.rdataclass.IN, dns.rdatatype.A, "1.2.3.4")
+        r2 = dns.rdtypes.IN.A.A(dns.rdataclass.IN, dns.rdatatype.A, b"\x01\x02\x03\x04")
+        r3 = dns.rdtypes.IN.A.A(
+            dns.rdataclass.IN, dns.rdatatype.A, ipaddress.ip_address("1.2.3.4")
+        )
+        self.assertEqual(r1, r2)
+        self.assertEqual(r1, r3)
+
+    def test_aaaa_rdata_made_multiple_ways(self):
+        r1 = dns.rdtypes.IN.AAAA.AAAA(dns.rdataclass.IN, dns.rdatatype.AAAA, "::1")
+        r2 = dns.rdtypes.IN.AAAA.AAAA(
+            dns.rdataclass.IN,
+            dns.rdatatype.AAAA,
+            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+        )
+        r3 = dns.rdtypes.IN.AAAA.AAAA(
+            dns.rdataclass.IN, dns.rdatatype.AAAA, ipaddress.ip_address("::1")
+        )
+        self.assertEqual(r1, r2)
+        self.assertEqual(r1, r3)
+
 
 class UtilTestCase(unittest.TestCase):
     def test_Gateway_bad_type0(self):