Skip to main content

mio/sys/unix/
net.rs

1use std::io;
2use std::mem::size_of;
3use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
4
5pub(crate) fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
6    let domain = match addr {
7        SocketAddr::V4(..) => libc::AF_INET,
8        SocketAddr::V6(..) => libc::AF_INET6,
9    };
10
11    new_socket(domain, socket_type)
12}
13
14/// Create a new non-blocking socket.
15pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> {
16    #[cfg(any(
17        target_os = "android",
18        target_os = "dragonfly",
19        target_os = "freebsd",
20        target_os = "hurd",
21        target_os = "illumos",
22        target_os = "linux",
23        target_os = "netbsd",
24        target_os = "openbsd",
25        target_os = "solaris",
26        target_os = "hermit",
27        target_os = "cygwin",
28    ))]
29    let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
30    // WASI doesn't have the concept of `fork`ing or `exec`ing processes, so
31    // `SOCK_CLOEXEC` neither exists nor is relevant:
32    #[cfg(target_os = "wasi")]
33    let socket_type = socket_type | libc::SOCK_NONBLOCK;
34    #[cfg(target_os = "nto")]
35    let socket_type = socket_type | libc::SOCK_CLOEXEC;
36
37    let socket = syscall!(socket(domain, socket_type, 0))?;
38
39    // Mimic `libstd` and set `SO_NOSIGPIPE` on apple systems.
40    #[cfg(any(
41        target_os = "ios",
42        target_os = "macos",
43        target_os = "tvos",
44        target_os = "visionos",
45        target_os = "watchos",
46    ))]
47    if let Err(err) = syscall!(setsockopt(
48        socket,
49        libc::SOL_SOCKET,
50        libc::SO_NOSIGPIPE,
51        &1 as *const libc::c_int as *const libc::c_void,
52        size_of::<libc::c_int>() as libc::socklen_t
53    )) {
54        let _ = syscall!(close(socket));
55        return Err(err);
56    }
57
58    // Darwin (and others) doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
59    #[cfg(any(
60        target_os = "aix",
61        target_os = "ios",
62        target_os = "macos",
63        target_os = "tvos",
64        target_os = "visionos",
65        target_os = "watchos",
66        target_os = "espidf",
67        target_os = "haiku",
68        target_os = "vita",
69        target_os = "nto",
70    ))]
71    {
72        if let Err(err) = syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) {
73            let _ = syscall!(close(socket));
74            return Err(err);
75        }
76        #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "nto")))]
77        if let Err(err) = syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)) {
78            let _ = syscall!(close(socket));
79            return Err(err);
80        }
81    }
82
83    Ok(socket)
84}
85
86/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
87/// SocketAddr* types into their system representation. The benefit of this specific
88/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
89/// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
90#[repr(C)]
91pub(crate) union SocketAddrCRepr {
92    v4: libc::sockaddr_in,
93    v6: libc::sockaddr_in6,
94}
95
96impl SocketAddrCRepr {
97    pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
98        self as *const _ as *const libc::sockaddr
99    }
100}
101
102/// Converts a Rust `SocketAddr` into the system representation.
103pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
104    match addr {
105        SocketAddr::V4(ref addr) => {
106            // `s_addr` is stored as BE on all machine and the array is in BE order.
107            // So the native endian conversion method is used so that it's never swapped.
108            let sin_addr = libc::in_addr {
109                s_addr: u32::from_ne_bytes(addr.ip().octets()),
110            };
111
112            let sockaddr_in = libc::sockaddr_in {
113                sin_family: libc::AF_INET as libc::sa_family_t,
114                sin_port: addr.port().to_be(),
115                sin_addr,
116                #[cfg(not(any(target_os = "haiku", target_os = "vita", target_os = "wasi")))]
117                sin_zero: [0; 8],
118                #[cfg(target_os = "haiku")]
119                sin_zero: [0; 24],
120                #[cfg(target_os = "vita")]
121                sin_zero: [0; 6],
122                #[cfg(any(
123                    target_os = "aix",
124                    target_os = "dragonfly",
125                    target_os = "freebsd",
126                    target_os = "haiku",
127                    target_os = "hurd",
128                    target_os = "ios",
129                    target_os = "macos",
130                    target_os = "netbsd",
131                    target_os = "openbsd",
132                    target_os = "tvos",
133                    target_os = "visionos",
134                    target_os = "watchos",
135                    target_os = "espidf",
136                    target_os = "vita",
137                    target_os = "hermit",
138                    target_os = "nto",
139                ))]
140                sin_len: 0,
141                #[cfg(target_os = "vita")]
142                sin_vport: addr.port().to_be(),
143            };
144
145            let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
146            let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t;
147            (sockaddr, socklen)
148        }
149        SocketAddr::V6(ref addr) => {
150            let sockaddr_in6 = libc::sockaddr_in6 {
151                sin6_family: libc::AF_INET6 as libc::sa_family_t,
152                sin6_port: addr.port().to_be(),
153                sin6_addr: libc::in6_addr {
154                    s6_addr: addr.ip().octets(),
155                },
156                sin6_flowinfo: addr.flowinfo(),
157                sin6_scope_id: addr.scope_id(),
158                #[cfg(any(
159                    target_os = "aix",
160                    target_os = "dragonfly",
161                    target_os = "freebsd",
162                    target_os = "haiku",
163                    target_os = "hurd",
164                    target_os = "ios",
165                    target_os = "macos",
166                    target_os = "netbsd",
167                    target_os = "openbsd",
168                    target_os = "tvos",
169                    target_os = "visionos",
170                    target_os = "watchos",
171                    target_os = "espidf",
172                    target_os = "vita",
173                    target_os = "nto",
174                    target_os = "hermit",
175                ))]
176                sin6_len: 0,
177                #[cfg(target_os = "vita")]
178                sin6_vport: addr.port().to_be(),
179                #[cfg(any(target_os = "illumos", target_os = "solaris"))]
180                __sin6_src_id: 0,
181            };
182
183            let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
184            let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
185            (sockaddr, socklen)
186        }
187    }
188}
189
190/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
191///
192/// # Safety
193///
194/// `storage` must have the `ss_family` field correctly initialized.
195/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
196pub(crate) unsafe fn to_socket_addr(
197    storage: *const libc::sockaddr_storage,
198) -> io::Result<SocketAddr> {
199    match (*storage).ss_family as libc::c_int {
200        libc::AF_INET => {
201            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
202            let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
203            let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
204            let port = u16::from_be(addr.sin_port);
205            Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
206        }
207        libc::AF_INET6 => {
208            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
209            let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
210            let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
211            let port = u16::from_be(addr.sin6_port);
212            Ok(SocketAddr::V6(SocketAddrV6::new(
213                ip,
214                port,
215                addr.sin6_flowinfo,
216                addr.sin6_scope_id,
217            )))
218        }
219        _ => Err(io::ErrorKind::InvalidInput.into()),
220    }
221}