|
| 1 | +//! Conversion between Rust and C representations of IP and Socket addresses. |
| 2 | +//! |
| 3 | +//! The intention is that the Rust networking API presents an interface using the types defined in |
| 4 | +//! [`core::net`](https://doc.rust-lang.org/stable/core/net/index.html). Conversion to and from the |
| 5 | +//! C types should only be required inside this crate. |
| 6 | +
|
| 7 | +use crate::error::{Error, Result}; |
| 8 | +use crate::raw::{in6_addr, in_addr, sockaddr, sockaddr_in, sockaddr_in6, socklen_t}; |
| 9 | +use core::mem::MaybeUninit; |
| 10 | +use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; |
| 11 | + |
| 12 | +/// Convert an IPv4 address from Rust to C representation |
| 13 | +pub(crate) fn ipv4_addr_to_c(addr: &Ipv4Addr) -> in_addr { |
| 14 | + let octets = addr.octets(); |
| 15 | + |
| 16 | + // SAFETY: It's not possible to easily construct this type due to the BindgenUnion field, and in |
| 17 | + // any case constructing it would always require unsafe code. |
| 18 | + // |
| 19 | + // The C type and the octets field of the Rust type both have a well-defined layout: 4 bytes in |
| 20 | + // big-endian order. So it is safe to transmute between the two types. |
| 21 | + unsafe { core::mem::transmute(octets) } |
| 22 | +} |
| 23 | + |
| 24 | +/// Convert an IPv4 address from C to Rust representation |
| 25 | +pub(crate) fn ipv4_addr_from_c(addr: &in_addr) -> Ipv4Addr { |
| 26 | + // SAFETY: The s4_addr union field has no restrictions on which values are valid, so is always |
| 27 | + // safe to access. |
| 28 | + let s4_addr = unsafe { addr.__bindgen_anon_1.s4_addr.as_ref() }; |
| 29 | + |
| 30 | + Ipv4Addr::new(s4_addr[0], s4_addr[1], s4_addr[2], s4_addr[3]) |
| 31 | +} |
| 32 | + |
| 33 | +/// Convert an IPv6 address from Rust to C representation |
| 34 | +pub(crate) fn ipv6_addr_to_c(addr: &Ipv6Addr) -> in6_addr { |
| 35 | + let octets = addr.octets(); |
| 36 | + |
| 37 | + // SAFETY: It's not possible to easily construct this type due to the BindgenUnion field, and in |
| 38 | + // any case constructing it would always require unsafe code. |
| 39 | + // |
| 40 | + // The C type and the octets field of the Rust type both have a well-defined layout: 16 bytes in |
| 41 | + // big-endian order. So it is safe to transmute between the two types. |
| 42 | + unsafe { core::mem::transmute(octets) } |
| 43 | +} |
| 44 | + |
| 45 | +/// Convert an IPv6 address from C to Rust representation |
| 46 | +pub(crate) fn ipv6_addr_from_c(addr: &in6_addr) -> Ipv6Addr { |
| 47 | + // SAFETY: The s6_addr16 union field has no restrictions on which values are valid, so is always |
| 48 | + // safe to access. |
| 49 | + let s6_addr16 = unsafe { addr.__bindgen_anon_1.s6_addr16.as_ref() }; |
| 50 | + |
| 51 | + Ipv6Addr::new( |
| 52 | + s6_addr16[0], |
| 53 | + s6_addr16[1], |
| 54 | + s6_addr16[2], |
| 55 | + s6_addr16[3], |
| 56 | + s6_addr16[4], |
| 57 | + s6_addr16[5], |
| 58 | + s6_addr16[6], |
| 59 | + s6_addr16[7], |
| 60 | + ) |
| 61 | +} |
| 62 | + |
| 63 | +/// Convert an IPv4 socket address from Rust to C representation |
| 64 | +pub(crate) fn sockaddr_v4_to_c(sa: &SocketAddrV4) -> sockaddr_in { |
| 65 | + sockaddr_in { |
| 66 | + sin_family: crate::raw::AF_INET as u16, |
| 67 | + sin_port: sa.port().to_be(), |
| 68 | + sin_addr: ipv4_addr_to_c(sa.ip()), |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +/// Try to convert an IPv4 socket address from C to Rust representation |
| 73 | +/// |
| 74 | +/// This will return an error if the `sin_family` field of `sa` is not `AF_INET`. |
| 75 | +pub(crate) fn try_sockaddr_v4_from_c(sa: &sockaddr_in) -> Result<SocketAddrV4> { |
| 76 | + if sa.sin_family != crate::raw::AF_INET as u16 { |
| 77 | + return Err(Error(crate::raw::EINVAL)); |
| 78 | + } |
| 79 | + |
| 80 | + Ok(SocketAddrV4::new( |
| 81 | + ipv4_addr_from_c(&sa.sin_addr), |
| 82 | + u16::from_be(sa.sin_port), |
| 83 | + )) |
| 84 | +} |
| 85 | + |
| 86 | +/// Try to convert an IPv6 socket address from Rust to C representation |
| 87 | +/// |
| 88 | +/// This will return an error in either of the following cases: |
| 89 | +/// - The `flowinfo` field is non-zero |
| 90 | +/// - The `scope_id` field cannot be represented as a `u8` |
| 91 | +/// |
| 92 | +/// Zephyr's `struct sockaddr_in6` differs slightly from the traditional BSD sockets API in that |
| 93 | +/// it does not contain a `flowinfo` field, and the `scope_id` is stored as a `uint8_t`. |
| 94 | +pub(crate) fn try_sockaddr_v6_to_c(sa: &SocketAddrV6) -> Result<sockaddr_in6> { |
| 95 | + if sa.flowinfo() != 0 || sa.scope_id() > u8::MAX as u32 { |
| 96 | + return Err(Error(crate::raw::EINVAL)); |
| 97 | + } |
| 98 | + |
| 99 | + Ok(sockaddr_in6 { |
| 100 | + sin6_family: crate::raw::AF_INET6 as u16, |
| 101 | + sin6_port: sa.port().to_be(), |
| 102 | + sin6_addr: ipv6_addr_to_c(sa.ip()), |
| 103 | + sin6_scope_id: sa.scope_id() as u8, |
| 104 | + }) |
| 105 | +} |
| 106 | + |
| 107 | +/// Try to convert an IPv6 socker address from C to Rust representation |
| 108 | +/// |
| 109 | +/// This will return an error if the `sin6_family` field of `sa` is not `AF_INET6`. |
| 110 | +pub(crate) fn try_sockaddr_v6_from_c(sa: &sockaddr_in6) -> Result<SocketAddrV6> { |
| 111 | + if sa.sin6_family != crate::raw::AF_INET6 as u16 { |
| 112 | + return Err(Error(crate::raw::EINVAL)); |
| 113 | + } |
| 114 | + |
| 115 | + Ok(SocketAddrV6::new( |
| 116 | + ipv6_addr_from_c(&sa.sin6_addr), |
| 117 | + u16::from_be(sa.sin6_port), |
| 118 | + 0, |
| 119 | + sa.sin6_scope_id as u32, |
| 120 | + )) |
| 121 | +} |
| 122 | + |
| 123 | +/// Try to convert a socket address from Rust to C representation |
| 124 | +/// |
| 125 | +/// This will return an error if the socket address is a `SocketAddrV6`, and this address contains |
| 126 | +/// fields which cannot be represented in the C struct (see [try_sockaddr_v6_from_c]). |
| 127 | +pub(crate) fn try_sockaddr_to_c(sa: &SocketAddr) -> Result<(sockaddr, socklen_t)> { |
| 128 | + let mut sa_out = MaybeUninit::<sockaddr>::zeroed(); |
| 129 | + let dst_ptr = sa_out.as_mut_ptr() as *mut u8; |
| 130 | + |
| 131 | + let socklen = match sa { |
| 132 | + SocketAddr::V4(sa4) => { |
| 133 | + let src = sockaddr_v4_to_c(sa4); |
| 134 | + let src_ptr = (&src as *const sockaddr_in) as *const u8; |
| 135 | + let len = core::mem::size_of::<sockaddr>(); |
| 136 | + |
| 137 | + // SAFETY: The `sockaddr` struct is sized to be able to contain either a `sockaddr_in` |
| 138 | + // or a `sockaddr_in6`, so it is safe to copy either type into it. |
| 139 | + unsafe { |
| 140 | + core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, len); |
| 141 | + } |
| 142 | + |
| 143 | + len |
| 144 | + } |
| 145 | + SocketAddr::V6(sa6) => { |
| 146 | + let src = try_sockaddr_v6_to_c(sa6)?; |
| 147 | + let src_ptr = (&src as *const sockaddr_in6) as *const u8; |
| 148 | + let len = core::mem::size_of::<sockaddr_in6>(); |
| 149 | + |
| 150 | + // SAFETY: The `sockaddr` struct is sized to be able to contain either a `sockaddr_in` |
| 151 | + // or a `sockaddr_in6`, so it is safe to copy either type into it. |
| 152 | + unsafe { |
| 153 | + core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, len); |
| 154 | + } |
| 155 | + |
| 156 | + len |
| 157 | + } |
| 158 | + }; |
| 159 | + |
| 160 | + // SAFETY: Both match arms initialise `sa_out` with a valid `sockaddr` |
| 161 | + let sa_out = unsafe { sa_out.assume_init() }; |
| 162 | + |
| 163 | + Ok((sa_out, socklen)) |
| 164 | +} |
| 165 | + |
| 166 | +/// Try to convert a socket address from C to Rust representation |
| 167 | +/// |
| 168 | +/// This will return an error if the `sa_family` field is not either `AF_INET` or `AF_INET6`. |
| 169 | +pub(crate) fn try_sockaddr_from_c(sa: &sockaddr, socklen: socklen_t) -> Result<SocketAddr> { |
| 170 | + match sa.sa_family as u32 { |
| 171 | + crate::raw::AF_INET => { |
| 172 | + if socklen != core::mem::size_of::<sockaddr_in>() { |
| 173 | + return Err(Error(crate::raw::EINVAL)); |
| 174 | + } |
| 175 | + |
| 176 | + let sa4_ref: &sockaddr_in = unsafe { core::mem::transmute(sa) }; |
| 177 | + let res = try_sockaddr_v4_from_c(sa4_ref)?; |
| 178 | + Ok(SocketAddr::V4(res)) |
| 179 | + } |
| 180 | + crate::raw::AF_INET6 => { |
| 181 | + if socklen != core::mem::size_of::<sockaddr_in6>() { |
| 182 | + return Err(Error(crate::raw::EINVAL)); |
| 183 | + } |
| 184 | + |
| 185 | + let sa6_ref: &sockaddr_in6 = unsafe { core::mem::transmute(sa) }; |
| 186 | + let res = try_sockaddr_v6_from_c(sa6_ref)?; |
| 187 | + Ok(SocketAddr::V6(res)) |
| 188 | + } |
| 189 | + _ => Err(Error(crate::raw::EINVAL)), |
| 190 | + } |
| 191 | +} |
0 commit comments