python / cpython

The Python programming language
https://www.python.org
Other
63.71k stars 30.53k forks source link

ipaddress IPv4Network and IPv6Network need specialized reverse_pointer property #125641

Open pemensik opened 1 month ago

pemensik commented 1 month ago

Bug report

Bug description:

# Add a code block here, if required
from ipaddress import ip_network

# Generates now:
# '8.4./.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'
assert(ip_network("2001:db8::/48").reverse_pointer == ['0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'])

# Generates now:
# '0/29.2.0.192.in-addr.arpa'
assert(ip_network("192.0.2.0/29").reverse_pointer == [
'0.2.0.192.in-addr.arpa', '1.2.0.192.in-addr.arpa', '2.2.0.192.in-addr.arpa', '3.2.0.192.in-addr.arpa', '4.2.0.192.in-addr.arpa',
'5.2.0.192.in-addr.arpa', '6.2.0.192.in-addr.arpa', '7.2.0.192.in-addr.arpa'])

IPv4Network and IPv6Network get their implementation from _BaseAddress. But for network, it does not work as it should. It returns broken nonsense instead of helping data. Problem is unlike normal IP address, for a network range, it may return just single string for prefix lengths divisible by 8 for IPv4 and 4 for IPv6. But for other prefixes, it should return a list of domain names used.

I have implemented working generator for a list result at https://github.com/InfrastructureServices/dnsconfd/pull/70 I think something similar should be used in base ipaddress directly. If reverse_pointer should not be fixed, it should be removed from networks instead.

Related to #123409, but that is not exactly about networks. Can be verified a bit using ipcalc --reverse-dns, but even that crashes on undivisible ipv6 prefixes.

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Zheaoli commented 1 month ago

Seems, we need to get more detail about the reverse pointer from the RFC.

@picnixz plz assign this issue to me, I will take handle of it .

picnixz commented 1 month ago

You can take care of the issue, I'm not here for the next days. I didn't know that the reverse pointer for network was different (clearly not my field of research) so I'm happy if you can fix the bug!

pemensik commented 1 month ago

There is no such thing as a reverse pointer for networks in dns itself. But it is related to how those domains can be delegated. Meaning where RFC 1035 can cut between zones operated by someone else. rfc2317 specifies recommended classless delegation using DNAME or CNAMEs in DNS.

Classless in-addr.arpa delegation describes how to delegate 1.0/25.2.0.192, therefore 192.0.2.0/25 network. Example from that page.

   $ORIGIN 2.0.192.in-addr.arpa.
   @       IN      SOA     my-ns.my.domain. hostmaster.my.domain. (...)
   ;...
   ;  <<0-127>> /25
   0/25            NS      ns.A.domain.
   0/25            NS      some.other.name.server.
   ;
   1               CNAME   1.0/25.2.0.192.in-addr.arpa.
   2               CNAME   2.0/25.2.0.192.in-addr.arpa.
   3               CNAME   3.0/25.2.0.192.in-addr.arpa.

This means that in 2.0.192.in-addr.arpa. zone, one needs to send ['0.2.0.192.in-addr.arpa.', '1.2.0.192.in-addr.arpa.', ... '127.2.0.192.in-addr.arpa.'] domains to some other domain. Those are the same, what I want generated by my proposal of . Special name 0/25.2.0.192.in-addr.arpa. is done to be able to delegate part of 2.0.192.in-addr.arpa. domain to DNS server operated by someone else. In that names 1.0/25.2.0.192.in-addr.arpa. to 127.0/25.2.0.192.in-addr.arpa. can be administered by different entity than original zone 2.0.192.in-addr.arpa..

There is no similar proposal for IPv6, because common IPv6 prefixes assigned to people are divided by 4 with zero remainder. That is /48, /52, /56, /60, /64. But prefix like /54 is still valid, but needs multiple domains to hold reverse addresses contained in that prefix.

ZeroIntensity commented 1 month ago

This is affecting all bugfix+ versions (3.12+), right?

Zheaoli commented 1 month ago

I think we just need to backport to 3.13 if this is necessary.

Zheaoli commented 4 weeks ago

I might want to ask some questions about this issue

in your test code

# '8.4./.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'
assert(ip_network("2001:db8::/48").reverse_pointer == ['0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'])

I'm not sure the 0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa behavior is defined in the RFC. So I would like to hear about your thought about IPV6 part.

pemensik commented 3 weeks ago

When I use ipcalc 2001:db8::/48, I get these ranges.

HostMin:    2001:db8::
HostMax:    2001:db8:0:ffff:ffff:ffff:ffff:ffff

Edit: the code is correct, '0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' should be correct prefix common for all those reversed domains contained in that range. network_address and broadcast_address properties of _BaseNetwork should contain similar values.

>>> import ipaddress
>>> n = ipaddress.ip_network("2001:db8::/48")
>>> n.broadcast_address
IPv6Address('2001:db8:0:ffff:ffff:ffff:ffff:ffff')
>>> n.broadcast_address.exploded
'2001:0db8:0000:ffff:ffff:ffff:ffff:ffff'
>>> n.network_address.exploded
'2001:0db8:0000:0000:0000:0000:0000:0000'
>>> n.network_address.exploded.replace(':','')[:n.prefixlen//4][::-1]
'00008bd01002'
>>> '.'.join(n.network_address.exploded.replace(':','')[:n.prefixlen//4][::-1])
'0.0.0.0.8.b.d.0.1.0.0.2'

Because prefix is divisible by 4, just a single domain is enough to hold all possible addresses. Something like that was merged into our dnsconfd.

pemensik commented 3 weeks ago

In case prefix is not divisible by 4, subnets contained in parent divisible by 4 need to be generated. In fact network_address.reverse_pointer can be reused and just proper number of label splitted from it. Our example is at:

https://github.com/InfrastructureServices/dnsconfd/blob/761c5fd37bd0d0b163a21fb6cabed14142004fcf/dnsconfd/network_objects/server_description.py#L190