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 | ))] |
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)] |
73 | pub(crate) union SocketAddrCRepr { |
74 | v4: libc::sockaddr_in, |
75 | v6: libc::sockaddr_in6, |
76 | } |
77 | |
78 | impl 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. |
85 | pub(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`. |
153 | pub(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 | |