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