From: Štěpán Balážik Date: Mon, 17 Mar 2025 14:58:16 +0000 (+0100) Subject: Allow built-in `IPv{4,6}Address` objects when constructing A/AAAA rdata (#1183) X-Git-Tag: v2.8.0rc1~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fee25b8ec2dcb323c33a6257099a23804a95a215;p=thirdparty%2Fdnspython.git Allow built-in `IPv{4,6}Address` objects when constructing A/AAAA rdata (#1183) * 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 --- diff --git a/dns/rdata.py b/dns/rdata.py index a305b8fa..6e5a6f31 100644 --- a/dns/rdata.py +++ b/dns/rdata.py @@ -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) diff --git a/tests/test_rdata.py b/tests/test_rdata.py index c1d3416c..8c4c5104 100644 --- a/tests/test_rdata.py +++ b/tests/test_rdata.py @@ -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):