Every IPv4 address is technically a valid IPv6 address. There's even a (relatively) standardized mapping between them: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
This wouldn't be breaking for SocketAddr
parsing, because every string could be first attempted to be parsed as IPv4 address, resulting in a SocketAddr::V4
variant, then as IPv6.
If however, you want to parse any legitimate IP address into an IPv6 address, you should be able to. There is a use-case for this, as IPv6 sockets on many OSes accept IPv4 mapped addresses. However, regardless of the address family of the socket.bind(...)
address your socket is bound to, all the send and receive methods take a SocketAddr
type. Which is fine, if you could pass in a SocketAddr::V6(<IPv4:port literal>.parse())
. But you can't, you have to manually attempt to parse the literal as SocketAddrV4
, then re-encode it as V6
through the SocketAddrV6::new
which requires you to specify the flowinfo and scope_id, without providing any defaults (which are obviously used in the string parsing).
Alternatively, this would be also simply solved if there was a From<SocketAddrV4> for SocketAddrV6
implementation in the std
, or a similar method if the mapping standard isn't definite enough.
#[cfg(test)]
mod tests {
use std::net::SocketAddrV6;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[test]
fn parse_ipv4_mapped_socket_address() -> Result<()> {
// succeeds
let _sock1: SocketAddrV6 = "[::1]:45454".parse()?;
let _sock2: SocketAddrV6 = "[::192.168.1.1]:45454".parse()?;
let _sock3: SocketAddrV6 = "[::ffff:192.168.1.1]:45454".parse()?;
// fails
let _sock4: SocketAddrV6 = "192.168.1.1:45454".parse()?;
Ok(())
}
}
Current workaround I use:
fn workaround(s: &str) -> std::result::Result<SocketAddrV6, std::net::AddrParseError> {
match s.parse::<SocketAddrV6>() {
Err(_) => match s.parse::<SocketAddrV4>() {
Ok(addr_v4) =>
format!("[::ffff:{}]:{}", addr_v4.ip(), addr_v4.port()).parse::<SocketAddrV6>(),
Err(e) => Err(e)
},
any => any
}
}