Skip to main content

nex_sys/
unix.rs

1use std::io;
2use std::mem;
3use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4use std::time::Duration;
5
6pub type CSocket = libc::c_int;
7pub type Buf = *const libc::c_void;
8pub type MutBuf = *mut libc::c_void;
9pub type BufLen = libc::size_t;
10pub type CouldFail = libc::ssize_t;
11pub type SockLen = libc::socklen_t;
12pub type MutSockLen = *mut libc::socklen_t;
13pub type SockAddr = libc::sockaddr;
14pub type SockAddrIn = libc::sockaddr_in;
15pub type SockAddrIn6 = libc::sockaddr_in6;
16pub type SockAddrStorage = libc::sockaddr_storage;
17pub type SockAddrFamily = libc::sa_family_t;
18pub type SockAddrFamily6 = libc::sa_family_t;
19pub type InAddr = libc::in_addr;
20pub type In6Addr = libc::in6_addr;
21
22#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "netbsd")))]
23pub type TvUsecType = libc::c_long;
24#[cfg(any(target_os = "macos", target_os = "ios", target_os = "netbsd"))]
25pub type TvUsecType = libc::c_int;
26
27pub const AF_INET: libc::c_int = libc::AF_INET;
28pub const AF_INET6: libc::c_int = libc::AF_INET6;
29
30pub use libc::{IFF_BROADCAST, IFF_LOOPBACK, IFF_MULTICAST, IFF_POINTOPOINT, IFF_UP};
31
32/// Close a raw socket/file descriptor.
33///
34/// # Safety
35///
36/// `sock` must be a valid descriptor owned by the caller. It must not be used
37/// again after this function returns.
38pub unsafe fn close(sock: CSocket) {
39    unsafe {
40        let _ = libc::close(sock);
41    }
42}
43
44fn ntohs(u: u16) -> u16 {
45    u16::from_be(u)
46}
47
48pub fn sockaddr_to_addr(storage: &SockAddrStorage, len: usize) -> io::Result<SocketAddr> {
49    match storage.ss_family as libc::c_int {
50        AF_INET => {
51            assert!(len >= mem::size_of::<SockAddrIn>());
52            let storage: &SockAddrIn = unsafe { mem::transmute(storage) };
53            let ip = ipv4_addr_int(storage.sin_addr);
54            // octets
55            let o1 = (ip >> 24) as u8;
56            let o2 = (ip >> 16) as u8;
57            let o3 = (ip >> 8) as u8;
58            let o4 = ip as u8;
59            let sockaddrv4 =
60                SocketAddrV4::new(Ipv4Addr::new(o1, o2, o3, o4), ntohs(storage.sin_port));
61            Ok(SocketAddr::V4(sockaddrv4))
62        }
63        AF_INET6 => {
64            assert!(len >= mem::size_of::<SockAddrIn6>());
65            let storage: &SockAddrIn6 = unsafe { mem::transmute(storage) };
66            let arr: [u16; 8] = unsafe { mem::transmute(storage.sin6_addr.s6_addr) };
67            // hextets
68            let h1 = ntohs(arr[0]);
69            let h2 = ntohs(arr[1]);
70            let h3 = ntohs(arr[2]);
71            let h4 = ntohs(arr[3]);
72            let h5 = ntohs(arr[4]);
73            let h6 = ntohs(arr[5]);
74            let h7 = ntohs(arr[6]);
75            let h8 = ntohs(arr[7]);
76            let ip = Ipv6Addr::new(h1, h2, h3, h4, h5, h6, h7, h8);
77            Ok(SocketAddr::V6(SocketAddrV6::new(
78                ip,
79                ntohs(storage.sin6_port),
80                u32::from_be(storage.sin6_flowinfo),
81                storage.sin6_scope_id,
82            )))
83        }
84        _ => Err(io::Error::new(io::ErrorKind::InvalidData, "Not supported")),
85    }
86}
87
88#[inline(always)]
89pub fn ipv4_addr_int(addr: InAddr) -> u32 {
90    addr.s_addr.to_be()
91}
92
93/// Convert a platform specific `timeval` into a Duration.
94pub fn timeval_to_duration(tv: libc::timeval) -> Duration {
95    Duration::new(tv.tv_sec as u64, (tv.tv_usec as u32) * 1000)
96}
97
98/// Convert a Duration into a platform specific `timeval`.
99pub fn duration_to_timeval(dur: Duration) -> libc::timeval {
100    libc::timeval {
101        tv_sec: dur.as_secs() as libc::time_t,
102        tv_usec: dur.subsec_micros() as TvUsecType,
103    }
104}
105
106/// Convert a platform specific `timespec` into a Duration.
107pub fn timespec_to_duration(ts: libc::timespec) -> Duration {
108    Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)
109}
110
111/// Convert a Duration into a platform specific `timespec`.
112pub fn duration_to_timespec(dur: Duration) -> libc::timespec {
113    libc::timespec {
114        tv_sec: dur.as_secs() as libc::time_t,
115        tv_nsec: (dur.subsec_nanos() as TvUsecType).into(),
116    }
117}
118
119/// Call `sendto(2)` using raw socket arguments.
120///
121/// # Safety
122///
123/// `buf` must be valid for reads of `len` bytes. `addr` must point to a valid
124/// socket address of length `addrlen`.
125pub unsafe fn sendto(
126    socket: CSocket,
127    buf: Buf,
128    len: BufLen,
129    flags: libc::c_int,
130    addr: *const SockAddr,
131    addrlen: SockLen,
132) -> CouldFail {
133    unsafe { libc::sendto(socket, buf, len, flags, addr, addrlen) }
134}
135
136/// Call `recvfrom(2)` using raw socket arguments.
137///
138/// # Safety
139///
140/// `buf` must be valid for writes of `len` bytes. `addr` and `addrlen` must
141/// point to writable storage for the returned socket address.
142pub unsafe fn recvfrom(
143    socket: CSocket,
144    buf: MutBuf,
145    len: BufLen,
146    flags: libc::c_int,
147    addr: *mut SockAddr,
148    addrlen: *mut SockLen,
149) -> CouldFail {
150    unsafe { libc::recvfrom(socket, buf, len, flags, addr, addrlen) }
151}
152
153#[inline]
154pub fn retry<F>(f: &mut F) -> libc::ssize_t
155where
156    F: FnMut() -> libc::ssize_t,
157{
158    loop {
159        let ret = f();
160        if ret != -1 || errno() as isize != libc::EINTR as isize {
161            return ret;
162        }
163    }
164}
165
166fn errno() -> i32 {
167    io::Error::last_os_error().raw_os_error().unwrap_or(0)
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use std::time::Duration;
174
175    #[test]
176    fn test_timeval_round_trip() {
177        let dur = Duration::new(1, 500_000_000);
178        let tv = duration_to_timeval(dur);
179        assert_eq!(timeval_to_duration(tv), dur);
180    }
181
182    #[test]
183    fn test_timespec_round_trip() {
184        let dur = Duration::new(2, 123_456_789);
185        let ts = duration_to_timespec(dur);
186        assert_eq!(timespec_to_duration(ts), dur);
187    }
188
189    #[test]
190    fn test_ipv4_addr_int() {
191        let addr = InAddr {
192            s_addr: u32::from_be(0x7f000001),
193        };
194        assert_eq!(ipv4_addr_int(addr), 0x7f000001);
195    }
196}