mellon85/external-ip

IP version support

Closed this issue · 4 comments

It would be nice to be able to choose between IPv4 and IPv6 address when requesting the address.

It looks like current implementation doesn't differentiate between IP versions, and returns whatever valid IpAddr the most/first successful source(s) return, which I think is based on whatever interface hyper chooses when sending the request. Having more control over which IP version to retrieve would be nice.

An interface can have multiple IP addresses associated (also of different families) so there are 2 issues to solve

  • The returned DNS records of the resolve will decide to which IP hyper will connect (and that the services will return)
  • The interface it will use depends on the default gateway of the host

I have no idea which of these services supports IPv6 or not (I don't have ipv6 routing) and can't test this. You could help if you can provide some examples on how these sources would reply if you connect to them via IPv6, even better a PR 😊

I, too, don't exactly have ipv6 routing, but connecting to a 5G network seems to give me a ipv6 address so I tested with that.

I ran this (modified version of HTTPSource::get_ip; I found api.ipify.org and bot.whatismyipaddress.com and added them to the list):

use hyper_tls::HttpsConnector;
use hyper::body::HttpBody;
use hyper::client::{Client, HttpConnector};

static SOURCES: [&str; 11] = [
    "https://icanhazip.com/",
    "https://myexternalip.com/raw",
    "https://ifconfig.io/ip",
    "https://ipecho.net/plain",
    "https://checkip.amazonaws.com/",
    "https://ident.me/",
    "http://whatismyip.akamai.com/",
    "https://myip.dnsomatic.com/",
    "https://diagnostic.opendns.com/myip",
    "https://api.ipify.org",
    "https://bot.whatismyipaddress.com"
];

#[tokio::main]
async fn main() {
    let mut http = HttpConnector::new();
    http.enforce_http(false);
    http.set_local_address(Some("0.0.0.0".parse().unwrap()));
    let https = HttpsConnector::new_with_connector(http);
    let client_ipv4 = Client::builder().build::<_, hyper::Body>(https);

    let mut http = HttpConnector::new();
    http.enforce_http(false);
    http.set_local_address(Some("::".parse().unwrap()));
    let https = HttpsConnector::new_with_connector(http);
    let client_ipv6 = Client::builder().build::<_, hyper::Body>(https);
    
    for client in &[client_ipv4, client_ipv6] {
    for &src in &SOURCES {
            let mut res = client.get(src.parse().unwrap()).await.expect("Failed to get res");

            let mut message = vec![];
            while let Some(chunk) = res.body_mut().data().await {
                message.extend(chunk.expect("Failed to get chunk"));
            }

            println!("{}: {}", src, std::str::from_utf8(&message).expect("res not utf8").trim());
        }
        println!("---");
    }
}

and got this:

https://icanhazip.com/: 223.39.203.195
https://myexternalip.com/raw: 223.39.203.195
https://ifconfig.io/ip: 223.39.203.195
https://ipecho.net/plain: 223.39.203.195
https://checkip.amazonaws.com/: 223.39.203.195
https://ident.me/: 223.39.203.195
http://whatismyip.akamai.com/: 223.39.203.195
https://myip.dnsomatic.com/: <html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx</center>
</body>
</html>
https://diagnostic.opendns.com/myip: 223.39.203.195
https://api.ipify.org: 223.39.203.195
https://bot.whatismyipaddress.com: 223.39.203.195
---
https://icanhazip.com/: 2001:2d8:6779:efe8:8d4e:3c18:6fb2:d6f7
https://myexternalip.com/raw: 223.39.203.194
https://ifconfig.io/ip: 2001:2d8:6779:efe8:8d4e:3c18:6fb2:d6f7
https://ipecho.net/plain: 223.39.203.194
https://checkip.amazonaws.com/: 223.39.203.194
https://ident.me/: 2001:2d8:6779:efe8:8d4e:3c18:6fb2:d6f7
http://whatismyip.akamai.com/: 223.39.203.194
https://myip.dnsomatic.com/: <html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx</center>
</body>
</html>
https://diagnostic.opendns.com/myip: 223.39.203.194
https://api.ipify.org: 223.39.203.194
https://bot.whatismyipaddress.com: 2001:2d8:6779:efe8:8d4e:3c18:6fb2:d6f7
---

Observations:

  • https://myip.dnsomatic.com/ often returns 429 Too Many Requests. Maybe remove them from builtin source?
  • Other than that, all requests using 0.0.0.0 return consistent IPv4 addresses.
  • Requests using :: return some consistent IPv4 addresses (different from when using 0.0.0.0) for some sources, and consistent IPv6 addresses for other source. Not sure what's happening here, but I think this is because those sources don't support IPv6.

Not sure how to deal with DNS sources, so I haven't tested those. Sorry.

For HTTP sources, it seems we can query using IPv6 local address and filter the responses to get external IPv6 address.

Any thoughts?

Thanks, I'll have a look at what can be changed