Skip to content

std::net::IpAddr: is_loopback failing on ipv4-in-ipv6 addresses  #69772

Open
@gh0st42

Description

@gh0st42

Summary:

The issue arises when trying to determine if an IpAddr is coming from localhost in a mixed IPv4/IPV6 environment.
The is_loopback function should return true for loopback IPs such as 127.0.0.1 or [::1]. This fails if a socket is bound to [::] which responds to IPv4 as well as IPv6. Here, IPv4 is automatically wrapped in IPv6 and 127.0.0.1 is becoming [::ffff:127.0.0.1] which is not recognized as a loopback address.

Detailed story

If I bind a server to 0.0.0.0 or [::1] and telnet/curl from localhost, I can easily tell whether the connection came from a local user or not by using the is_loopback call on IpAddr, Ipv4Addr or Ipv6Addr.

Once I bind my server to [::] to work on IPv4 AND IPv6 at the same time and then connect to it via v4 to 127.0.0.1 the is_loopback call returns false.
I then have to manually try conversion of the Ipv6Addr into an Ipv4Addr (using to_ipv4) and perform a second check with is_loopback.

In my opinion, this behavior should either be clearly documented in the standard library or better yet should happen automatically (at least in IpAddr) since an ipv6 encapsulated ipv4 loopback address is still a perfectly valid loopback address.

The current documentation in Ipv6Addr states that it is a check for [::1] but a clear statement that IPv4 in IPv6 loopback addresses are not covered might help. I also guess that having the current minimal checks in both variants (v4 and v6) make sense to keep but the general is_loopback in IpAddr itself could provide the convenient conversion as it covers v4 and v6 anyways.

Example Code

use std::net::{Ipv4Addr, Ipv6Addr};

fn main() {
    let ipv4 = Ipv4Addr::new(127, 0, 0, 1); // regular 127.0.0.1
    let ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1); // regular [::1] 
    let v4_in_v6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1); // if binding socket to [::] and connecting from ipv4 localhost
    
    println!("{} is loopback? {} ", ipv4, ipv4.is_loopback());
    println!("{} is loopback? {} ", ipv6, ipv6.is_loopback());
    println!("{} is loopback? {} ", v4_in_v6, v4_in_v6.is_loopback());
    println!("{} is loopback? {} ", v4_in_v6, v4_in_v6.to_ipv4().unwrap().is_loopback());
}

(Playground)

Output:

127.0.0.1 is loopback? true 
::1 is loopback? true 
::ffff:127.0.0.1 is loopback? false 
::ffff:127.0.0.1 is loopback? true 

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions