Skip to main content

x11rb/rust_connection/
stream.rs

1use rustix::fd::{AsFd, BorrowedFd};
2use std::io::{IoSlice, Result};
3use std::net::TcpStream;
4#[cfg(unix)]
5use std::os::unix::io::{AsRawFd, IntoRawFd, OwnedFd, RawFd};
6#[cfg(unix)]
7use std::os::unix::net::UnixStream;
8#[cfg(windows)]
9use std::os::windows::io::{
10    AsRawSocket, AsSocket, BorrowedSocket, IntoRawSocket, OwnedSocket, RawSocket,
11};
12
13use crate::utils::RawFdContainer;
14use x11rb_protocol::parse_display::ConnectAddress;
15use x11rb_protocol::xauth::Family;
16
17/// The kind of operation that one want to poll for.
18#[derive(Debug, Clone, Copy)]
19pub enum PollMode {
20    /// Check if the stream is readable, i.e. there is pending data to be read.
21    Readable,
22
23    /// Check if the stream is writable, i.e. some data could be successfully written to it.
24    Writable,
25
26    /// Check for both readability and writability.
27    ReadAndWritable,
28}
29
30impl PollMode {
31    /// Does this poll mode include readability?
32    pub fn readable(self) -> bool {
33        match self {
34            PollMode::Readable | PollMode::ReadAndWritable => true,
35            PollMode::Writable => false,
36        }
37    }
38
39    /// Does this poll mode include writability?
40    pub fn writable(self) -> bool {
41        match self {
42            PollMode::Writable | PollMode::ReadAndWritable => true,
43            PollMode::Readable => false,
44        }
45    }
46}
47
48/// A trait used to implement the raw communication with the X11 server.
49///
50/// None of the functions of this trait shall return [`std::io::ErrorKind::Interrupted`].
51/// If a system call fails with this error, the implementation should try again.
52pub trait Stream {
53    /// Waits for level-triggered read and/or write events on the stream.
54    ///
55    /// This function does not return what caused it to complete the poll.
56    /// Instead, callers should try to read or write and check for
57    /// [`std::io::ErrorKind::WouldBlock`].
58    ///
59    /// This function is allowed to spuriously return even if the stream
60    /// is neither readable nor writable. However, it shall not do it
61    /// continuously, which would cause a 100% CPU usage.
62    ///
63    /// # Multithreading
64    ///
65    /// If `Self` is `Send + Sync` and `poll` is used concurrently from more than
66    /// one thread, all threads should wake when the stream becomes readable (when
67    /// `read` is `true`) or writable (when `write` is `true`).
68    fn poll(&self, mode: PollMode) -> Result<()>;
69
70    /// Read some bytes and FDs from this reader without blocking, returning how many bytes
71    /// were read.
72    ///
73    /// This function works like [`std::io::Read::read`], but also supports the reception of file
74    /// descriptors. Any received file descriptors are appended to the given `fd_storage`.
75    /// Whereas implementation of [`std::io::Read::read`] are allowed to block or not to block,
76    /// this method shall never block and return `ErrorKind::WouldBlock` if needed.
77    ///
78    /// This function does not guarantee that all file descriptors were sent together with the data
79    /// with which they are received. However, file descriptors may not be received later than the
80    /// data that was sent at the same time. Instead, file descriptors may only be received
81    /// earlier.
82    ///
83    /// # Multithreading
84    ///
85    /// If `Self` is `Send + Sync` and `read` is used concurrently from more than one thread:
86    ///
87    /// * Both the data and the file descriptors shall be read in order, but possibly
88    ///   interleaved across threads.
89    /// * Neither the data nor the file descriptors shall be duplicated.
90    /// * The returned value shall always be the actual number of bytes read into `buf`.
91    fn read(&self, buf: &mut [u8], fd_storage: &mut Vec<RawFdContainer>) -> Result<usize>;
92
93    /// Write a buffer and some FDs into this writer without blocking, returning how many
94    /// bytes were written.
95    ///
96    /// This function works like [`std::io::Write::write`], but also supports sending file
97    /// descriptors. The `fds` argument contains the file descriptors to send. The order of file
98    /// descriptors is maintained. Whereas implementation of [`std::io::Write::write`] are
99    /// allowed to block or not to block, this function must never block and return
100    /// `ErrorKind::WouldBlock` if needed.
101    ///
102    /// This function does not guarantee that all file descriptors are sent together with the data.
103    /// Any file descriptors that were sent are removed from the beginning of the given `Vec`.
104    ///
105    /// There is no guarantee that the given file descriptors are received together with the given
106    /// data. File descriptors might be received earlier than their corresponding data. It is not
107    /// allowed for file descriptors to be received later than the bytes that were sent at the same
108    /// time.
109    ///
110    /// # Multithreading
111    ///
112    /// If `Self` is `Send + Sync` and `write` is used concurrently from more than one thread:
113    ///
114    /// * Both the data and the file descriptors shall be written in order, but possibly
115    ///   interleaved across threads.
116    /// * Neither the data nor the file descriptors shall be duplicated.
117    /// * The returned value shall always be the actual number of bytes written from `buf`.
118    fn write(&self, buf: &[u8], fds: &mut Vec<RawFdContainer>) -> Result<usize>;
119
120    /// Like `write`, except that it writes from a slice of buffers. Like `write`, this
121    /// method must never block.
122    ///
123    /// This method must behave as a call to `write` with the buffers concatenated would.
124    ///
125    /// The default implementation calls `write` with the first nonempty buffer provided.
126    ///
127    /// # Multithreading
128    ///
129    /// Same as `write`.
130    fn write_vectored(&self, bufs: &[IoSlice<'_>], fds: &mut Vec<RawFdContainer>) -> Result<usize> {
131        for buf in bufs {
132            if !buf.is_empty() {
133                return self.write(buf, fds);
134            }
135        }
136        Ok(0)
137    }
138}
139
140/// A wrapper around a `TcpStream` or `UnixStream`.
141///
142/// Use by default in `RustConnection` as stream.
143#[derive(Debug)]
144pub struct DefaultStream {
145    inner: DefaultStreamInner,
146}
147
148#[cfg(unix)]
149type DefaultStreamInner = RawFdContainer;
150
151#[cfg(not(unix))]
152type DefaultStreamInner = TcpStream;
153
154/// The address of a peer in a format suitable for xauth.
155///
156/// These values can be directly given to [`x11rb_protocol::xauth::get_auth`].
157type PeerAddr = (Family, Vec<u8>);
158
159impl DefaultStream {
160    /// Try to connect to the X11 server described by the given arguments.
161    pub fn connect(addr: &ConnectAddress<'_>) -> Result<(Self, PeerAddr)> {
162        match addr {
163            ConnectAddress::Hostname(host, port) => {
164                // connect over TCP
165                let stream = TcpStream::connect((*host, *port))?;
166                Self::from_tcp_stream(stream)
167            }
168            #[cfg(unix)]
169            ConnectAddress::Socket(path) => {
170                // connect over Unix domain socket
171                let stream = UnixStream::connect(path)?;
172                Self::from_unix_stream(stream)
173            }
174            #[cfg(not(unix))]
175            ConnectAddress::Socket(_) => {
176                // Unix domain sockets are not supported on Windows
177                Err(std::io::Error::new(
178                    std::io::ErrorKind::Other,
179                    "Unix domain sockets are not supported on Windows",
180                ))
181            }
182            _ => Err(std::io::Error::new(
183                std::io::ErrorKind::Other,
184                "The given address family is not implemented",
185            )),
186        }
187    }
188
189    /// Creates a new `Stream` from an already connected `TcpStream`.
190    ///
191    /// The stream will be set in non-blocking mode.
192    ///
193    /// This returns the peer address in a format suitable for [`x11rb_protocol::xauth::get_auth`].
194    pub fn from_tcp_stream(stream: TcpStream) -> Result<(Self, PeerAddr)> {
195        let peer_addr = peer_addr::tcp(&stream.peer_addr()?);
196        stream.set_nonblocking(true)?;
197        let result = Self {
198            inner: stream.into(),
199        };
200        Ok((result, peer_addr))
201    }
202
203    /// Creates a new `Stream` from an already connected `UnixStream`.
204    ///
205    /// The stream will be set in non-blocking mode.
206    ///
207    /// This returns the peer address in a format suitable for [`x11rb_protocol::xauth::get_auth`].
208    #[cfg(unix)]
209    pub fn from_unix_stream(stream: UnixStream) -> Result<(Self, PeerAddr)> {
210        stream.set_nonblocking(true)?;
211        let result = Self {
212            inner: stream.into(),
213        };
214        Ok((result, peer_addr::local()))
215    }
216
217    fn as_fd(&self) -> BorrowedFd<'_> {
218        self.inner.as_fd()
219    }
220}
221
222#[cfg(unix)]
223impl AsRawFd for DefaultStream {
224    fn as_raw_fd(&self) -> RawFd {
225        self.inner.as_raw_fd()
226    }
227}
228
229#[cfg(unix)]
230impl AsFd for DefaultStream {
231    fn as_fd(&self) -> BorrowedFd<'_> {
232        self.inner.as_fd()
233    }
234}
235
236#[cfg(unix)]
237impl IntoRawFd for DefaultStream {
238    fn into_raw_fd(self) -> RawFd {
239        self.inner.into_raw_fd()
240    }
241}
242
243#[cfg(unix)]
244impl From<DefaultStream> for OwnedFd {
245    fn from(stream: DefaultStream) -> Self {
246        stream.inner
247    }
248}
249
250#[cfg(windows)]
251impl AsRawSocket for DefaultStream {
252    fn as_raw_socket(&self) -> RawSocket {
253        self.inner.as_raw_socket()
254    }
255}
256
257#[cfg(windows)]
258impl AsSocket for DefaultStream {
259    fn as_socket(&self) -> BorrowedSocket<'_> {
260        self.inner.as_socket()
261    }
262}
263
264#[cfg(windows)]
265impl IntoRawSocket for DefaultStream {
266    fn into_raw_socket(self) -> RawSocket {
267        self.inner.into_raw_socket()
268    }
269}
270
271#[cfg(windows)]
272impl From<DefaultStream> for OwnedSocket {
273    fn from(stream: DefaultStream) -> Self {
274        stream.inner.into()
275    }
276}
277
278#[cfg(unix)]
279fn do_write(
280    stream: &DefaultStream,
281    bufs: &[IoSlice<'_>],
282    fds: &mut Vec<RawFdContainer>,
283) -> Result<usize> {
284    use rustix::io::Errno;
285    use rustix::net::{sendmsg, SendAncillaryBuffer, SendAncillaryMessage, SendFlags};
286    use std::mem::MaybeUninit;
287
288    fn sendmsg_wrapper(
289        fd: BorrowedFd<'_>,
290        iov: &[IoSlice<'_>],
291        cmsgs: &mut SendAncillaryBuffer<'_, '_, '_>,
292        flags: SendFlags,
293    ) -> Result<usize> {
294        loop {
295            match sendmsg(fd, iov, cmsgs, flags) {
296                Ok(n) => return Ok(n),
297                // try again
298                Err(Errno::INTR) => {}
299                Err(e) => return Err(e.into()),
300            }
301        }
302    }
303
304    let fd = stream.as_fd();
305
306    let res = if !fds.is_empty() {
307        let fds = fds.iter().map(|fd| fd.as_fd()).collect::<Vec<_>>();
308        let rights = SendAncillaryMessage::ScmRights(&fds);
309
310        let mut cmsg_space = vec![MaybeUninit::uninit(); rights.size()];
311        let mut cmsg_buffer = SendAncillaryBuffer::new(&mut cmsg_space);
312        assert!(cmsg_buffer.push(rights));
313
314        sendmsg_wrapper(fd, bufs, &mut cmsg_buffer, SendFlags::empty())?
315    } else {
316        sendmsg_wrapper(fd, bufs, &mut Default::default(), SendFlags::empty())?
317    };
318
319    // We successfully sent all FDs
320    fds.clear();
321
322    Ok(res)
323}
324
325impl Stream for DefaultStream {
326    fn poll(&self, mode: PollMode) -> Result<()> {
327        use rustix::event::{poll, PollFd, PollFlags};
328        use rustix::io::Errno;
329
330        let mut poll_flags = PollFlags::empty();
331        if mode.readable() {
332            poll_flags |= PollFlags::IN;
333        }
334        if mode.writable() {
335            poll_flags |= PollFlags::OUT;
336        }
337        let fd = self.as_fd();
338        let mut poll_fds = [PollFd::from_borrowed_fd(fd, poll_flags)];
339        loop {
340            match poll(&mut poll_fds, None) {
341                Ok(_) => break,
342                Err(Errno::INTR) => {}
343                Err(e) => return Err(e.into()),
344            }
345        }
346        // Let the errors (POLLERR) be handled when trying to read or write.
347        Ok(())
348    }
349
350    fn read(&self, buf: &mut [u8], fd_storage: &mut Vec<RawFdContainer>) -> Result<usize> {
351        #[cfg(unix)]
352        {
353            use rustix::io::Errno;
354            use rustix::net::{recvmsg, RecvAncillaryBuffer, RecvAncillaryMessage};
355            use std::io::IoSliceMut;
356            use std::mem::MaybeUninit;
357
358            // 1024 bytes on the stack should be enough for more file descriptors than the X server will ever
359            // send, as well as the header for the ancillary data. If you can find a case where this can
360            // overflow with an actual production X11 server, I'll buy you a steak dinner.
361            let mut cmsg = [MaybeUninit::uninit(); 1024];
362            let mut iov = [IoSliceMut::new(buf)];
363            let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg);
364
365            let fd = self.as_fd();
366            let msg = loop {
367                match recvmsg(fd, &mut iov, &mut cmsg_buffer, recvmsg::flags()) {
368                    Ok(msg) => break msg,
369                    // try again
370                    Err(Errno::INTR) => {}
371                    Err(e) => return Err(e.into()),
372                }
373            };
374
375            let fds_received = cmsg_buffer
376                .drain()
377                .filter_map(|cmsg| match cmsg {
378                    RecvAncillaryMessage::ScmRights(r) => Some(r),
379                    _ => None,
380                })
381                .flatten();
382
383            let mut cloexec_error = Ok(());
384            fd_storage.extend(recvmsg::after_recvmsg(fds_received, &mut cloexec_error));
385            cloexec_error?;
386
387            Ok(msg.bytes)
388        }
389        #[cfg(not(unix))]
390        {
391            use std::io::Read;
392            // No FDs are read, so nothing needs to be done with fd_storage
393            let _ = fd_storage;
394            loop {
395                // Use `impl Read for &TcpStream` to avoid needing a mutable `TcpStream`.
396                match (&mut &self.inner).read(buf) {
397                    Ok(n) => return Ok(n),
398                    // try again
399                    Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
400                    Err(e) => return Err(e),
401                }
402            }
403        }
404    }
405
406    fn write(&self, buf: &[u8], fds: &mut Vec<RawFdContainer>) -> Result<usize> {
407        #[cfg(unix)]
408        {
409            do_write(self, &[IoSlice::new(buf)], fds)
410        }
411        #[cfg(not(unix))]
412        {
413            use std::io::{Error, ErrorKind, Write};
414            if !fds.is_empty() {
415                return Err(Error::new(ErrorKind::Other, "FD passing is unsupported"));
416            }
417            loop {
418                // Use `impl Write for &TcpStream` to avoid needing a mutable `TcpStream`.
419                match (&mut &self.inner).write(buf) {
420                    Ok(n) => return Ok(n),
421                    // try again
422                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
423                    Err(e) => return Err(e),
424                }
425            }
426        }
427    }
428
429    fn write_vectored(&self, bufs: &[IoSlice<'_>], fds: &mut Vec<RawFdContainer>) -> Result<usize> {
430        #[cfg(unix)]
431        {
432            do_write(self, bufs, fds)
433        }
434        #[cfg(not(unix))]
435        {
436            use std::io::{Error, ErrorKind, Write};
437            if !fds.is_empty() {
438                return Err(Error::new(ErrorKind::Other, "FD passing is unsupported"));
439            }
440            loop {
441                // Use `impl Write for &TcpStream` to avoid needing a mutable `TcpStream`.
442                match (&mut &self.inner).write_vectored(bufs) {
443                    Ok(n) => return Ok(n),
444                    // try again
445                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
446                    Err(e) => return Err(e),
447                }
448            }
449        }
450    }
451}
452
453/// Helper code to make sure that received FDs are marked as CLOEXEC
454#[cfg(any(
455    target_os = "android",
456    target_os = "dragonfly",
457    target_os = "freebsd",
458    target_os = "linux",
459    target_os = "netbsd",
460    target_os = "openbsd"
461))]
462mod recvmsg {
463    use super::RawFdContainer;
464    use rustix::net::RecvFlags;
465
466    pub(crate) fn flags() -> RecvFlags {
467        RecvFlags::CMSG_CLOEXEC
468    }
469
470    pub(crate) fn after_recvmsg<'a>(
471        fds: impl Iterator<Item = RawFdContainer> + 'a,
472        _cloexec_error: &'a mut Result<(), rustix::io::Errno>,
473    ) -> impl Iterator<Item = RawFdContainer> + 'a {
474        fds
475    }
476}
477
478/// Helper code to make sure that received FDs are marked as CLOEXEC
479#[cfg(all(
480    unix,
481    not(any(
482        target_os = "android",
483        target_os = "dragonfly",
484        target_os = "freebsd",
485        target_os = "linux",
486        target_os = "netbsd",
487        target_os = "openbsd"
488    ))
489))]
490mod recvmsg {
491    use super::RawFdContainer;
492    use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags};
493    use rustix::net::RecvFlags;
494
495    pub(crate) fn flags() -> RecvFlags {
496        RecvFlags::empty()
497    }
498
499    pub(crate) fn after_recvmsg<'a>(
500        fds: impl Iterator<Item = RawFdContainer> + 'a,
501        cloexec_error: &'a mut rustix::io::Result<()>,
502    ) -> impl Iterator<Item = RawFdContainer> + 'a {
503        fds.map(move |fd| {
504            if let Err(e) =
505                fcntl_getfd(&fd).and_then(|flags| fcntl_setfd(&fd, flags | FdFlags::CLOEXEC))
506            {
507                *cloexec_error = Err(e);
508            }
509            fd
510        })
511    }
512}
513
514mod peer_addr {
515    use super::{Family, PeerAddr};
516    use std::net::{Ipv4Addr, SocketAddr};
517
518    // Get xauth information representing a local connection
519    pub(super) fn local() -> PeerAddr {
520        let hostname = gethostname::gethostname()
521            .to_str()
522            .map_or_else(Vec::new, |s| s.as_bytes().to_vec());
523        (Family::LOCAL, hostname)
524    }
525
526    // Get xauth information representing a TCP connection to the given address
527    pub(super) fn tcp(addr: &SocketAddr) -> PeerAddr {
528        let ip = match addr {
529            SocketAddr::V4(addr) => *addr.ip(),
530            SocketAddr::V6(addr) => {
531                let ip = addr.ip();
532                if ip.is_loopback() {
533                    // This is a local connection.
534                    // Use LOCALHOST to cause a fall-through in the code below.
535                    Ipv4Addr::LOCALHOST
536                } else if let Some(ip) = ip.to_ipv4() {
537                    // Let the ipv4 code below handle this
538                    ip
539                } else {
540                    // Okay, this is really a v6 address
541                    return (Family::INTERNET6, ip.octets().to_vec());
542                }
543            }
544        };
545
546        // Handle the v4 address
547        if ip.is_loopback() {
548            local()
549        } else {
550            (Family::INTERNET, ip.octets().to_vec())
551        }
552    }
553}