]>
Commit | Line | Data |
---|---|---|
fe692bf9 FG |
1 | //! IPv4, IPv6, and Socket addresses. |
2 | ||
3 | use super::super::c; | |
4 | #[cfg(unix)] | |
5 | use crate::ffi::CStr; | |
6 | #[cfg(unix)] | |
7 | use crate::io; | |
8 | #[cfg(unix)] | |
9 | use crate::path; | |
10 | #[cfg(not(windows))] | |
11 | use core::convert::TryInto; | |
12 | #[cfg(unix)] | |
13 | use core::fmt; | |
14 | #[cfg(unix)] | |
15 | use core::slice; | |
16 | ||
17 | /// `struct sockaddr_un` | |
18 | #[cfg(unix)] | |
19 | #[derive(Clone)] | |
20 | #[doc(alias = "sockaddr_un")] | |
21 | pub struct SocketAddrUnix { | |
22 | pub(crate) unix: c::sockaddr_un, | |
23 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
24 | len: c::socklen_t, | |
25 | } | |
26 | ||
27 | #[cfg(unix)] | |
28 | impl SocketAddrUnix { | |
29 | /// Construct a new Unix-domain address from a filesystem path. | |
30 | #[inline] | |
31 | pub fn new<P: path::Arg>(path: P) -> io::Result<Self> { | |
32 | path.into_with_c_str(Self::_new) | |
33 | } | |
34 | ||
35 | #[inline] | |
36 | fn _new(path: &CStr) -> io::Result<Self> { | |
37 | let mut unix = Self::init(); | |
38 | let bytes = path.to_bytes_with_nul(); | |
39 | if bytes.len() > unix.sun_path.len() { | |
40 | return Err(io::Errno::NAMETOOLONG); | |
41 | } | |
42 | for (i, b) in bytes.iter().enumerate() { | |
43 | unix.sun_path[i] = *b as c::c_char; | |
44 | } | |
45 | ||
46 | #[cfg(any(bsd, target_os = "haiku"))] | |
47 | { | |
48 | unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap(); | |
49 | } | |
50 | ||
51 | Ok(Self { | |
52 | unix, | |
53 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
54 | len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(), | |
55 | }) | |
56 | } | |
57 | ||
58 | /// Construct a new abstract Unix-domain address from a byte slice. | |
59 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
60 | #[inline] | |
61 | pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> { | |
62 | let mut unix = Self::init(); | |
63 | if 1 + name.len() > unix.sun_path.len() { | |
64 | return Err(io::Errno::NAMETOOLONG); | |
65 | } | |
66 | unix.sun_path[0] = b'\0' as c::c_char; | |
67 | for (i, b) in name.iter().enumerate() { | |
68 | unix.sun_path[1 + i] = *b as c::c_char; | |
69 | } | |
70 | let len = offsetof_sun_path() + 1 + name.len(); | |
71 | let len = len.try_into().unwrap(); | |
72 | Ok(Self { | |
73 | unix, | |
74 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
75 | len, | |
76 | }) | |
77 | } | |
78 | ||
79 | fn init() -> c::sockaddr_un { | |
80 | c::sockaddr_un { | |
81 | #[cfg(any(bsd, target_os = "haiku"))] | |
82 | sun_len: 0, | |
83 | sun_family: c::AF_UNIX as _, | |
84 | #[cfg(bsd)] | |
85 | sun_path: [0; 104], | |
86 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
87 | sun_path: [0; 108], | |
88 | #[cfg(target_os = "haiku")] | |
89 | sun_path: [0; 126], | |
90 | } | |
91 | } | |
92 | ||
93 | /// For a filesystem path address, return the path. | |
94 | #[inline] | |
95 | pub fn path(&self) -> Option<&CStr> { | |
96 | let len = self.len(); | |
97 | if len != 0 && self.unix.sun_path[0] != b'\0' as c::c_char { | |
98 | let end = len as usize - offsetof_sun_path(); | |
99 | let bytes = &self.unix.sun_path[..end]; | |
100 | // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. And | |
101 | // `from_bytes_with_nul_unchecked` since the string is NUL-terminated. | |
102 | unsafe { | |
103 | Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts( | |
104 | bytes.as_ptr().cast(), | |
105 | bytes.len(), | |
106 | ))) | |
107 | } | |
108 | } else { | |
109 | None | |
110 | } | |
111 | } | |
112 | ||
113 | /// For an abstract address, return the identifier. | |
114 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
115 | #[inline] | |
116 | pub fn abstract_name(&self) -> Option<&[u8]> { | |
117 | let len = self.len(); | |
118 | if len != 0 && self.unix.sun_path[0] == b'\0' as c::c_char { | |
119 | let end = len as usize - offsetof_sun_path(); | |
120 | let bytes = &self.unix.sun_path[1..end]; | |
121 | // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. | |
122 | unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) } | |
123 | } else { | |
124 | None | |
125 | } | |
126 | } | |
127 | ||
128 | #[inline] | |
129 | pub(crate) fn addr_len(&self) -> c::socklen_t { | |
130 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
131 | { | |
132 | self.len | |
133 | } | |
134 | #[cfg(any(bsd, target_os = "haiku"))] | |
135 | { | |
136 | c::socklen_t::from(self.unix.sun_len) | |
137 | } | |
138 | } | |
139 | ||
140 | #[inline] | |
141 | pub(crate) fn len(&self) -> usize { | |
142 | self.addr_len() as usize | |
143 | } | |
144 | } | |
145 | ||
146 | #[cfg(unix)] | |
147 | impl PartialEq for SocketAddrUnix { | |
148 | #[inline] | |
149 | fn eq(&self, other: &Self) -> bool { | |
150 | let self_len = self.len() - offsetof_sun_path(); | |
151 | let other_len = other.len() - offsetof_sun_path(); | |
152 | self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len]) | |
153 | } | |
154 | } | |
155 | ||
156 | #[cfg(unix)] | |
157 | impl Eq for SocketAddrUnix {} | |
158 | ||
159 | #[cfg(unix)] | |
160 | impl PartialOrd for SocketAddrUnix { | |
161 | #[inline] | |
162 | fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | |
163 | let self_len = self.len() - offsetof_sun_path(); | |
164 | let other_len = other.len() - offsetof_sun_path(); | |
165 | self.unix.sun_path[..self_len].partial_cmp(&other.unix.sun_path[..other_len]) | |
166 | } | |
167 | } | |
168 | ||
169 | #[cfg(unix)] | |
170 | impl Ord for SocketAddrUnix { | |
171 | #[inline] | |
172 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { | |
173 | let self_len = self.len() - offsetof_sun_path(); | |
174 | let other_len = other.len() - offsetof_sun_path(); | |
175 | self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len]) | |
176 | } | |
177 | } | |
178 | ||
179 | #[cfg(unix)] | |
180 | impl core::hash::Hash for SocketAddrUnix { | |
181 | #[inline] | |
182 | fn hash<H: core::hash::Hasher>(&self, state: &mut H) { | |
183 | let self_len = self.len() - offsetof_sun_path(); | |
184 | self.unix.sun_path[..self_len].hash(state) | |
185 | } | |
186 | } | |
187 | ||
188 | #[cfg(unix)] | |
189 | impl fmt::Debug for SocketAddrUnix { | |
190 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
191 | if let Some(path) = self.path() { | |
192 | path.fmt(fmt) | |
193 | } else { | |
194 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
195 | if let Some(name) = self.abstract_name() { | |
196 | return name.fmt(fmt); | |
197 | } | |
198 | ||
199 | "(unnamed)".fmt(fmt) | |
200 | } | |
201 | } | |
202 | } | |
203 | ||
204 | /// `struct sockaddr_storage` as a raw struct. | |
205 | pub type SocketAddrStorage = c::sockaddr_storage; | |
206 | ||
207 | /// Return the offset of the `sun_path` field of `sockaddr_un`. | |
208 | #[cfg(not(windows))] | |
209 | #[inline] | |
210 | pub(crate) fn offsetof_sun_path() -> usize { | |
211 | let z = c::sockaddr_un { | |
212 | #[cfg(any(bsd, target_os = "haiku"))] | |
213 | sun_len: 0_u8, | |
214 | #[cfg(any(bsd, target_os = "haiku"))] | |
215 | sun_family: 0_u8, | |
216 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
217 | sun_family: 0_u16, | |
218 | #[cfg(bsd)] | |
219 | sun_path: [0; 104], | |
220 | #[cfg(not(any(bsd, target_os = "haiku")))] | |
221 | sun_path: [0; 108], | |
222 | #[cfg(target_os = "haiku")] | |
223 | sun_path: [0; 126], | |
224 | }; | |
225 | (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize) | |
226 | } |