gio/
inet_address.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Take a look at the license at the top of the repository in the LICENSE file.

use std::net::IpAddr;

use glib::{prelude::*, translate::*};

use crate::{ffi, prelude::*, InetAddress, SocketFamily};

#[derive(Debug)]
pub enum InetAddressBytes<'a> {
    V4(&'a [u8; 4]),
    V6(&'a [u8; 16]),
}

impl InetAddressBytes<'_> {
    #[inline]
    fn deref(&self) -> &[u8] {
        use self::InetAddressBytes::*;

        match *self {
            V4(bytes) => bytes,
            V6(bytes) => bytes,
        }
    }
}

impl InetAddress {
    /// Creates a new #GInetAddress from the given @family and @bytes.
    /// @bytes should be 4 bytes for [`SocketFamily::Ipv4`][crate::SocketFamily::Ipv4] and 16 bytes for
    /// [`SocketFamily::Ipv6`][crate::SocketFamily::Ipv6].
    /// ## `bytes`
    /// raw address data
    /// ## `family`
    /// the address family of @bytes
    ///
    /// # Returns
    ///
    /// a new #GInetAddress corresponding to @family and @bytes.
    ///     Free the returned object with g_object_unref().
    #[doc(alias = "g_inet_address_new_from_bytes")]
    pub fn from_bytes(inet_address_bytes: InetAddressBytes) -> Self {
        let bytes = inet_address_bytes.deref();

        let family = match inet_address_bytes {
            InetAddressBytes::V4(_) => SocketFamily::Ipv4,
            InetAddressBytes::V6(_) => SocketFamily::Ipv6,
        };
        unsafe {
            from_glib_full(ffi::g_inet_address_new_from_bytes(
                bytes.to_glib_none().0,
                family.into_glib(),
            ))
        }
    }
}

mod sealed {
    pub trait Sealed {}
    impl<T: super::IsA<super::InetAddress>> Sealed for T {}
}

pub trait InetAddressExtManual: sealed::Sealed + IsA<InetAddress> + 'static {
    // rustdoc-stripper-ignore-next
    /// Returns `None` in case the address has a native size different than 4 and 16.
    // rustdoc-stripper-ignore-next-stop
    /// Gets the raw binary address data from @self.
    ///
    /// # Returns
    ///
    /// a pointer to an internal array of the bytes in @self,
    /// which should not be modified, stored, or freed. The size of this
    /// array can be gotten with g_inet_address_get_native_size().
    #[doc(alias = "g_inet_address_to_bytes")]
    #[inline]
    fn to_bytes(&self) -> Option<InetAddressBytes<'_>> {
        let size = self.native_size();
        unsafe {
            let bytes = ffi::g_inet_address_to_bytes(self.as_ref().to_glib_none().0);
            if size == 4 {
                Some(InetAddressBytes::V4(&*(bytes as *const [u8; 4])))
            } else if size == 16 {
                Some(InetAddressBytes::V6(&*(bytes as *const [u8; 16])))
            } else {
                None
            }
        }
    }
}

impl<O: IsA<InetAddress>> InetAddressExtManual for O {}

impl From<IpAddr> for InetAddress {
    fn from(addr: IpAddr) -> Self {
        match addr {
            IpAddr::V4(v4) => Self::from_bytes(InetAddressBytes::V4(&v4.octets())),
            IpAddr::V6(v6) => Self::from_bytes(InetAddressBytes::V6(&v6.octets())),
        }
    }
}

impl From<InetAddress> for IpAddr {
    fn from(addr: InetAddress) -> Self {
        match addr.to_bytes() {
            Some(InetAddressBytes::V4(bytes)) => IpAddr::from(*bytes),
            Some(InetAddressBytes::V6(bytes)) => IpAddr::from(*bytes),
            None => panic!("Unknown IP kind"),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::IpAddr;

    use crate::InetAddress;

    #[test]
    fn test_ipv6_to_rust() {
        let rust_addr = "2606:50c0:8000::153".parse::<IpAddr>().unwrap();
        assert!(rust_addr.is_ipv6());
        let gio_addr = InetAddress::from(rust_addr);
        assert_eq!(rust_addr, IpAddr::from(gio_addr));
    }

    #[test]
    fn test_ipv4_to_rust() {
        let rust_addr = "185.199.108.153".parse::<IpAddr>().unwrap();
        assert!(rust_addr.is_ipv4());
        let gio_addr = InetAddress::from(rust_addr);
        assert_eq!(rust_addr, IpAddr::from(gio_addr));
    }
}