Convert hex socket notation to IP and Port

What is the most Rust idiomatic way of converting a hex string consisting of IP:Port to a standard IP and Port pair?

0100007F:0019

Given the above I want to get to IP of 127.0.0.1 and port of 25. Need to be able to handle IPv4 and IPv6.

I've tried SocketAddr::parse with no luck.

Thanks

You have to first parse an integer from the string representing it in base (radix) 16, and then you can just use From/Into to go from a u32 to a Ipv4Address:

use ::std::{*,
    io::BufRead,
};

fn main ()
{
    // 1. Read a line
    let line = {
        let mut s = String::new();
        io::stdin()
            .read_line(&mut s)
            .expect("Failed to read line")
        ;
        s
    };
    dbg!(&line);

    // 2. Split that line over `:` and parse two hex numbers
    //    out of it
    let (ip, port) =
        match line
                .split(':')
                .map(str::trim)
                .map(|s| {
                    u32::from_str_radix(s, 0x10)
                        .expect("hex number")
                })
                .collect::<::arrayvec::ArrayVec<[_; 2]>>()
                [..]
        {
            | [ip, port] => dbg!((ip, port)),
            | _          => panic!("Invalid input!"),
        }
    ;

    // 3. Convert a u32 to a `Ipv4Adddr` with the natural conversion
    let ip = net::Ipv4Addr::from(ip);
    dbg!(ip, port);
}
2 Likes

One other question.

How do I convert the IP hex number from big endian to little? IP is reversed.

Thanks

You can use u32::to_le/u32::to_be (there are similar methods for all integer primitives)

1 Like

Thanks, I must have looked right over them.

If you know the bytes are reversed, you can also use u32's .swap_bytes() method.

1 Like

Is there a built in function to reverse the bytes, not of the whole value, but every, say, 32 bits. So if I had a 128bit value it would reverse the byte order in each of the four 32 bit boundaries and not the whole 128 bit value?

I have a 128 bit value I need to do this with. I read it as a string and then have to convert it to an IPv6 address but am running into this issue.

That's too specific for it to be available in the stdlib. You can look for a crate doing that, or implement it yourself:

  • without using unsafe:

    pub
    fn u128_swap_u32s_then_to_ipv6 (n: u128)
      -> ::std::net::Ipv6Addr
    {
        use ::arrayvec::ArrayVec;
    
        // Split u128 into four u32s
        let mut u32s: ArrayVec<[u32; 4]> =
            (0 .. 4)
                .rev()
                .map(|i| (n >> (32 * i)) as u32)
                .collect()
        ;
        
        // Swap the bytes of each u32
        u32s.iter_mut()
            .for_each(|n: &mut u32| *n = n.swap_bytes())
        ;
        
        // Convert each u32 into four u8s using network endianness
        let u8s: ArrayVec<[[u8; 4]; 4]> =
            u32s.into_iter()
                .map(u32::to_ne_bytes)
                .collect()
        ;
        
        // flatten the u8s
        let u8s: [u8; 16] = ArrayVec::into_inner(
            u8s .iter()
                .flat_map(|it| it.iter().copied())
                .collect()
        ).unwrap();
    
        // Convert the u8s into an Ipv6 address
        ::std::net::Ipv6Addr::from(u8s)
    }
    
  • with unsafe:

    fn u128_swap_u32s_then_to_ipv6 (mut n: u128)
      -> ::std::net::Ipv6Addr
    {
        unsafe { &mut *(&mut n as *mut u128 as *mut [u32; 4]) }
            .iter_mut()
            .for_each(|n: &mut u32| *n = n.swap_bytes())
        ;
        ::std::net::Ipv6Addr::from(n)
    }
    
  • Playground

1 Like

Thanks so much again, was getting there but your solution is better. Need to get better at closures.

To close this thread I had to update the function provided by Yandros as it was reversing the dwords twice.

Thanks to Yandros and KrishnaSannasi for the help. I'm learning and loving Rust, but still a lot to learn.

pub fn u128_swap_u32s_then_to_ipv6 (n: u128) -> std::io::Result<::std::net::Ipv6Addr> {
use ::arrayvec::ArrayVec;

// Split u128 into four u32s
let u32s: ArrayVec<[u32; 4]> =
        (0 .. 4)
        .rev()
        .map(|i| (n >> (32 * i)) as u32)
        .collect();

// Convert each u32 into four u8s using network endianness
let u8s: ArrayVec<[[u8; 4]; 4]> =
    u32s.into_iter()
        .map(u32::to_ne_bytes)
        .collect()
;

// flatten the u8s
let u8s: [u8; 16] = ArrayVec::into_inner(
    u8s.iter()
        .flat_map(|it| it.iter().copied())
        .collect()
).unwrap();

// Convert the u8s into an Ipv6 address
Ok(::std::net::Ipv6Addr::from(u8s))

}

2 Likes

It seems I misunderstood your request :sweat_smile:
Good job fixing it, this kind of "mini-problems" make for very good learning exercises, so keep going in that direction :slight_smile:

1 Like