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}