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: i32 = 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 = "illumos",
21 target_os = "linux",
22 target_os = "netbsd",
23 target_os = "openbsd",
24 ))]
25 let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
26
27 let socket = syscall!(socket(domain, socket_type, 0))?;
28
29 // Mimick `libstd` and set `SO_NOSIGPIPE` on apple systems.
30 #[cfg(any(
31 target_os = "ios",
32 target_os = "macos",
33 target_os = "tvos",
34 target_os = "watchos",
35 ))]
36 if let Err(err) = syscall!(setsockopt(
37 socket,
38 libc::SOL_SOCKET,
39 libc::SO_NOSIGPIPE,
40 &1 as *const libc::c_int as *const libc::c_void,
41 size_of::<libc::c_int>() as libc::socklen_t
42 )) {
43 let _ = syscall!(close(socket));
44 return Err(err);
45 }
46
47 // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC.
48 #[cfg(any(
49 target_os = "ios",
50 target_os = "macos",
51 target_os = "tvos",
52 target_os = "watchos",
53 ))]
54 {
55 if let Err(err) = syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK)) {
56 let _ = syscall!(close(socket));
57 return Err(err);
58 }
59 if let Err(err) = syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)) {
60 let _ = syscall!(close(socket));
61 return Err(err);
62 }
63 }
64
65 Ok(socket)
66}
67
68/// A type with the same memory layout as `libc::sockaddr`. Used in converting Rust level
69/// SocketAddr* types into their system representation. The benefit of this specific
70/// type over using `libc::sockaddr_storage` is that this type is exactly as large as it
71/// needs to be and not a lot larger. And it can be initialized cleaner from Rust.
72#[repr(C)]
73pub(crate) union SocketAddrCRepr {
74 v4: libc::sockaddr_in,
75 v6: libc::sockaddr_in6,
76}
77
78impl SocketAddrCRepr {
79 pub(crate) fn as_ptr(&self) -> *const libc::sockaddr {
80 self as *const _ as *const libc::sockaddr
81 }
82}
83
84/// Converts a Rust `SocketAddr` into the system representation.
85pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_t) {
86 match addr {
87 SocketAddr::V4(ref addr) => {
88 // `s_addr` is stored as BE on all machine and the array is in BE order.
89 // So the native endian conversion method is used so that it's never swapped.
90 let sin_addr = libc::in_addr {
91 s_addr: u32::from_ne_bytes(addr.ip().octets()),
92 };
93
94 let sockaddr_in = libc::sockaddr_in {
95 sin_family: libc::AF_INET as libc::sa_family_t,
96 sin_port: addr.port().to_be(),
97 sin_addr,
98 sin_zero: [0; 8],
99 #[cfg(any(
100 target_os = "dragonfly",
101 target_os = "freebsd",
102 target_os = "ios",
103 target_os = "macos",
104 target_os = "netbsd",
105 target_os = "openbsd",
106 target_os = "tvos",
107 target_os = "watchos",
108 ))]
109 sin_len: 0,
110 };
111
112 let sockaddr = SocketAddrCRepr { v4: sockaddr_in };
113 let socklen = size_of::<libc::sockaddr_in>() as libc::socklen_t;
114 (sockaddr, socklen)
115 }
116 SocketAddr::V6(ref addr) => {
117 let sockaddr_in6 = libc::sockaddr_in6 {
118 sin6_family: libc::AF_INET6 as libc::sa_family_t,
119 sin6_port: addr.port().to_be(),
120 sin6_addr: libc::in6_addr {
121 s6_addr: addr.ip().octets(),
122 },
123 sin6_flowinfo: addr.flowinfo(),
124 sin6_scope_id: addr.scope_id(),
125 #[cfg(any(
126 target_os = "dragonfly",
127 target_os = "freebsd",
128 target_os = "ios",
129 target_os = "macos",
130 target_os = "netbsd",
131 target_os = "openbsd",
132 target_os = "tvos",
133 target_os = "watchos",
134 ))]
135 sin6_len: 0,
136 #[cfg(target_os = "illumos")]
137 __sin6_src_id: 0,
138 };
139
140 let sockaddr = SocketAddrCRepr { v6: sockaddr_in6 };
141 let socklen = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
142 (sockaddr, socklen)
143 }
144 }
145}
146
147/// Converts a `libc::sockaddr` compatible struct into a native Rust `SocketAddr`.
148///
149/// # Safety
150///
151/// `storage` must have the `ss_family` field correctly initialized.
152/// `storage` must be initialised to a `sockaddr_in` or `sockaddr_in6`.
153pub(crate) unsafe fn to_socket_addr(
154 storage: *const libc::sockaddr_storage,
155) -> io::Result<SocketAddr> {
156 match (*storage).ss_family as libc::c_int {
157 libc::AF_INET => {
158 // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
159 let addr: &libc::sockaddr_in = &*(storage as *const libc::sockaddr_in);
160 let ip: Ipv4Addr = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
161 let port: u16 = u16::from_be(addr.sin_port);
162 Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
163 }
164 libc::AF_INET6 => {
165 // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
166 let addr: &libc::sockaddr_in6 = &*(storage as *const libc::sockaddr_in6);
167 let ip: Ipv6Addr = Ipv6Addr::from(addr.sin6_addr.s6_addr);
168 let port: u16 = u16::from_be(addr.sin6_port);
169 Ok(SocketAddr::V6(SocketAddrV6::new(
170 ip,
171 port,
172 addr.sin6_flowinfo,
173 addr.sin6_scope_id,
174 )))
175 }
176 _ => Err(io::ErrorKind::InvalidInput.into()),
177 }
178}
179