gio/unix_socket_address.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#[cfg(not(docsrs))]
4use std::ffi::OsStr;
5#[cfg(unix)]
6#[cfg(not(docsrs))]
7use std::os::unix::ffi::OsStrExt;
8use std::{path, ptr, slice};
9
10use glib::translate::*;
11
12use crate::{ffi, prelude::*, SocketAddress, UnixSocketAddress, UnixSocketAddressType};
13
14#[derive(Debug)]
15pub enum UnixSocketAddressPath<'a> {
16 Path(&'a path::Path),
17 Anonymous,
18 Abstract(&'a [u8]),
19 AbstractPadded(&'a [u8]),
20}
21
22impl UnixSocketAddressPath<'_> {
23 fn to_type(&self) -> UnixSocketAddressType {
24 use self::UnixSocketAddressPath::*;
25
26 match *self {
27 Path(_) => UnixSocketAddressType::Path,
28 Anonymous => UnixSocketAddressType::Anonymous,
29 Abstract(_) => UnixSocketAddressType::Abstract,
30 AbstractPadded(_) => UnixSocketAddressType::AbstractPadded,
31 }
32 }
33}
34
35impl UnixSocketAddress {
36 /// Creates a new #GUnixSocketAddress for @path.
37 ///
38 /// To create abstract socket addresses, on systems that support that,
39 /// use g_unix_socket_address_new_abstract().
40 /// ## `path`
41 /// the socket path
42 ///
43 /// # Returns
44 ///
45 /// a new #GUnixSocketAddress
46 #[doc(alias = "g_unix_socket_address_new")]
47 pub fn new(path: &path::Path) -> UnixSocketAddress {
48 unsafe {
49 SocketAddress::from_glib_full(ffi::g_unix_socket_address_new(path.to_glib_none().0))
50 .unsafe_cast()
51 }
52 }
53
54 /// Creates a new #GUnixSocketAddress of type @type_ with name @path.
55 ///
56 /// If @type_ is [`UnixSocketAddressType::Path`][crate::UnixSocketAddressType::Path], this is equivalent to
57 /// calling g_unix_socket_address_new().
58 ///
59 /// If @type_ is [`UnixSocketAddressType::Anonymous`][crate::UnixSocketAddressType::Anonymous], @path and @path_len will be
60 /// ignored.
61 ///
62 /// If @path_type is [`UnixSocketAddressType::Abstract`][crate::UnixSocketAddressType::Abstract], then @path_len
63 /// bytes of @path will be copied to the socket's path, and only those
64 /// bytes will be considered part of the name. (If @path_len is -1,
65 /// then @path is assumed to be NUL-terminated.) For example, if @path
66 /// was "test", then calling g_socket_address_get_native_size() on the
67 /// returned socket would return 7 (2 bytes of overhead, 1 byte for the
68 /// abstract-socket indicator byte, and 4 bytes for the name "test").
69 ///
70 /// If @path_type is [`UnixSocketAddressType::AbstractPadded`][crate::UnixSocketAddressType::AbstractPadded], then
71 /// @path_len bytes of @path will be copied to the socket's path, the
72 /// rest of the path will be padded with 0 bytes, and the entire
73 /// zero-padded buffer will be considered the name. (As above, if
74 /// @path_len is -1, then @path is assumed to be NUL-terminated.) In
75 /// this case, g_socket_address_get_native_size() will always return
76 /// the full size of a `struct sockaddr_un`, although
77 /// g_unix_socket_address_get_path_len() will still return just the
78 /// length of @path.
79 ///
80 /// [`UnixSocketAddressType::Abstract`][crate::UnixSocketAddressType::Abstract] is preferred over
81 /// [`UnixSocketAddressType::AbstractPadded`][crate::UnixSocketAddressType::AbstractPadded] for new programs. Of course,
82 /// when connecting to a server created by another process, you must
83 /// use the appropriate type corresponding to how that process created
84 /// its listening socket.
85 /// ## `path`
86 /// the name
87 /// ## `type_`
88 /// a #GUnixSocketAddressType
89 ///
90 /// # Returns
91 ///
92 /// a new #GUnixSocketAddress
93 #[doc(alias = "g_unix_socket_address_new_with_type")]
94 pub fn with_type(address_type: UnixSocketAddressPath) -> Self {
95 use self::UnixSocketAddressPath::*;
96
97 let type_ = address_type.to_type();
98 let new = |ptr, len| unsafe {
99 SocketAddress::from_glib_full(ffi::g_unix_socket_address_new_with_type(
100 ptr,
101 len,
102 type_.into_glib(),
103 ))
104 .unsafe_cast()
105 };
106 match address_type {
107 Path(path) => new(path.to_glib_none().0, -1),
108 Abstract(path) | AbstractPadded(path) => new(
109 path.to_glib_none().0 as *mut libc::c_char,
110 path.len() as i32,
111 ),
112 Anonymous => new(ptr::null_mut(), 0),
113 }
114 }
115}
116
117mod sealed {
118 pub trait Sealed {}
119 impl<T: super::IsA<super::UnixSocketAddress>> Sealed for T {}
120}
121
122pub trait UnixSocketAddressExtManual: sealed::Sealed + IsA<UnixSocketAddress> + 'static {
123 /// Gets @self's path, or for abstract sockets the "name".
124 ///
125 /// Guaranteed to be zero-terminated, but an abstract socket
126 /// may contain embedded zeros, and thus you should use
127 /// g_unix_socket_address_get_path_len() to get the true length
128 /// of this string.
129 ///
130 /// # Returns
131 ///
132 /// the path for @self
133 #[doc(alias = "g_unix_socket_address_get_path")]
134 #[doc(alias = "get_path")]
135 fn path(&self) -> Option<UnixSocketAddressPath> {
136 use self::UnixSocketAddressPath::*;
137
138 let path = unsafe {
139 let path = ffi::g_unix_socket_address_get_path(self.as_ref().to_glib_none().0);
140 if path.is_null() || self.path_len() == 0 {
141 &[]
142 } else {
143 slice::from_raw_parts(path as *const u8, self.path_len())
144 }
145 };
146 match self.address_type() {
147 UnixSocketAddressType::Anonymous => Some(Anonymous),
148 #[cfg(not(docsrs))]
149 UnixSocketAddressType::Path => Some(Path(path::Path::new(OsStr::from_bytes(path)))),
150 #[cfg(docsrs)]
151 UnixSocketAddressType::Path => unreachable!(),
152 UnixSocketAddressType::Abstract => Some(Abstract(path)),
153 UnixSocketAddressType::AbstractPadded => Some(AbstractPadded(path)),
154 UnixSocketAddressType::Invalid | UnixSocketAddressType::__Unknown(_) => None,
155 }
156 }
157}
158
159impl<O: IsA<UnixSocketAddress>> UnixSocketAddressExtManual for O {}
160
161#[cfg(test)]
162mod test {
163 use super::*;
164
165 // Check the actual path and len are correct and are not the underlying OsString
166 #[test]
167 fn check_path() {
168 let mut os_string = std::ffi::OsString::with_capacity(100);
169 os_string.push("/tmp/foo");
170 let path = os_string.as_ref();
171
172 let addr = UnixSocketAddress::new(path);
173 assert_eq!(addr.path_len(), 8);
174 assert_eq!(addr.path_as_array().unwrap().as_ref(), b"/tmp/foo");
175
176 let addr = UnixSocketAddress::with_type(UnixSocketAddressPath::Path(path));
177 assert_eq!(addr.path_len(), 8);
178 assert_eq!(addr.path_as_array().unwrap().as_ref(), b"/tmp/foo");
179 }
180}