| 1 | //! Contains utilities for connection to the X11 server. |
| 2 | |
| 3 | use crate::errors::{ConnectError, ParseError}; |
| 4 | use crate::protocol::xproto::{Setup, SetupAuthenticate, SetupFailed, SetupRequest}; |
| 5 | use crate::x11_utils::{Serialize, TryParse}; |
| 6 | |
| 7 | #[cfg (feature = "std" )] |
| 8 | use crate::xauth::{get_auth, Family}; |
| 9 | |
| 10 | use alloc::{vec, vec::Vec}; |
| 11 | |
| 12 | use core::fmt; |
| 13 | |
| 14 | /// The connection handshake used to connect to the X11 server. |
| 15 | /// |
| 16 | /// In order to connect to the X11 server, the client must send the |
| 17 | /// server a request containing important pieces of client data. In |
| 18 | /// response, the server sends the client a response containing one |
| 19 | /// of the following: |
| 20 | /// |
| 21 | /// - An error indicating that the setup request is malformed, or the |
| 22 | /// setup otherwise failed. |
| 23 | /// - A request for further authorization data. |
| 24 | /// - The [`Setup`](protocol/xproto/struct.Setup.html) for the connection, |
| 25 | /// which contains server-specific information and is necessary for |
| 26 | /// the client's ability to communicate with the server. |
| 27 | /// |
| 28 | /// This handshake contains four relevant methods: |
| 29 | /// |
| 30 | /// - `new`, which creates the handshake and also returns the setup request |
| 31 | /// to send to the server. |
| 32 | /// - `buffer`, which returns an `&mut [u8]` containing the buffer |
| 33 | /// which is intended to hold the bytes received from the server. |
| 34 | /// - `advance`, which takes a `usize` indicating how many bytes |
| 35 | /// were received from the server and advances the buffer. |
| 36 | /// - `into_setup`, which consumes this `Connect` and returns the |
| 37 | /// full `Setup`. |
| 38 | /// |
| 39 | /// # Examples |
| 40 | /// |
| 41 | /// Let's say you have an object `stream` which implements `Read` |
| 42 | /// and `Write`. In addition, you already have the connection family, |
| 43 | /// the address of the connection, and the display. You can use the `Connect` |
| 44 | /// to establish an X11 connection like so: |
| 45 | /// |
| 46 | /// ```rust,no_run |
| 47 | /// # use x11rb_protocol::connect::Connect; |
| 48 | /// # use x11rb_protocol::xauth::Family; |
| 49 | /// # use std::{error::Error, io::prelude::*}; |
| 50 | /// # fn main() -> Result<(), Box<dyn Error>> { |
| 51 | /// # struct Stream; |
| 52 | /// # impl Read for Stream { |
| 53 | /// # fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
| 54 | /// # Ok(buf.len()) |
| 55 | /// # } |
| 56 | /// # } |
| 57 | /// # impl Write for Stream { |
| 58 | /// # fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
| 59 | /// # Ok(buf.len()) |
| 60 | /// # } |
| 61 | /// # fn flush(&mut self) -> std::io::Result<()> { |
| 62 | /// # Ok(()) |
| 63 | /// # } |
| 64 | /// # } |
| 65 | /// # let mut stream = Stream; |
| 66 | /// let family = Family::INTERNET; |
| 67 | /// let address = b"foobar" ; |
| 68 | /// let display = 0; |
| 69 | /// |
| 70 | /// let (mut connect, setup_request) = Connect::new(family, address, display)?; |
| 71 | /// |
| 72 | /// // send the setup request to the server |
| 73 | /// stream.write_all(&setup_request)?; |
| 74 | /// |
| 75 | /// // receive the setup response from the server |
| 76 | /// loop { |
| 77 | /// let adv = stream.read(connect.buffer())?; |
| 78 | /// |
| 79 | /// // if we've completed the setup, break out of the loop |
| 80 | /// if connect.advance(adv) { |
| 81 | /// break; |
| 82 | /// } |
| 83 | /// } |
| 84 | /// |
| 85 | /// // get the setup used for our connection |
| 86 | /// let setup = connect.into_setup()?; |
| 87 | /// # Ok(()) |
| 88 | /// # } |
| 89 | /// ``` |
| 90 | /// |
| 91 | /// If, instead, `stream` implements `AsyncRead` and `AsyncWrite`, the code |
| 92 | /// would be identical, but with `.await` after `read` and `write_all`. |
| 93 | pub struct Connect { |
| 94 | // input buffer |
| 95 | buffer: Vec<u8>, |
| 96 | // position in the buffer that has been filled |
| 97 | advanced: usize, |
| 98 | } |
| 99 | |
| 100 | const INITIAL_CAPACITY: usize = 8; |
| 101 | |
| 102 | // X11 interprets capital B as big endian, and lowercase l as little endian. |
| 103 | #[cfg (target_endian = "little" )] |
| 104 | const BYTE_ORDER: u8 = b'l' ; |
| 105 | #[cfg (not(target_endian = "little" ))] |
| 106 | const BYTE_ORDER: u8 = b'B' ; |
| 107 | |
| 108 | // protocol version |
| 109 | const PROTOCOL_MAJOR_VERSION: u16 = 11; |
| 110 | const PROTOCOL_MINOR_VERSION: u16 = 0; |
| 111 | |
| 112 | impl Connect { |
| 113 | /// The initial state of a `Connect`. |
| 114 | fn blank() -> Self { |
| 115 | Self { |
| 116 | buffer: vec![0; INITIAL_CAPACITY], |
| 117 | advanced: 0, |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /// Create a new `Connect` from the given authorization data. |
| 122 | /// |
| 123 | /// This uses the provided protocol name and data to establish the connection, |
| 124 | /// rather than the default protocol name and data found in `Xauthority`. |
| 125 | /// |
| 126 | /// # Example |
| 127 | /// |
| 128 | /// ```rust |
| 129 | /// # use x11rb_protocol::connect::Connect; |
| 130 | /// |
| 131 | /// let (connect, setup_request) = Connect::with_authorization( |
| 132 | /// b"MIT-MAGIC-COOKIE-1" .to_vec(), |
| 133 | /// b"my_secret_password" .to_vec(), |
| 134 | /// ); |
| 135 | /// ``` |
| 136 | pub fn with_authorization(protocol_name: Vec<u8>, protocol_data: Vec<u8>) -> (Self, Vec<u8>) { |
| 137 | // craft the setup request |
| 138 | let sr = SetupRequest { |
| 139 | byte_order: BYTE_ORDER, |
| 140 | protocol_major_version: PROTOCOL_MAJOR_VERSION, |
| 141 | protocol_minor_version: PROTOCOL_MINOR_VERSION, |
| 142 | authorization_protocol_name: protocol_name, |
| 143 | authorization_protocol_data: protocol_data, |
| 144 | }; |
| 145 | |
| 146 | // return it |
| 147 | (Self::blank(), sr.serialize()) |
| 148 | } |
| 149 | |
| 150 | /// Create a new `Connect` from the information necessary to connect to the X11 server. |
| 151 | /// |
| 152 | /// This returns the connection handshake object as well as the setup request to send to the server. |
| 153 | #[cfg (feature = "std" )] |
| 154 | pub fn new( |
| 155 | family: Family, |
| 156 | address: &[u8], |
| 157 | display: u16, |
| 158 | ) -> Result<(Self, Vec<u8>), ConnectError> { |
| 159 | match get_auth(family, address, display)? { |
| 160 | Some((name, data)) => Ok(Self::with_authorization(name, data)), |
| 161 | None => { |
| 162 | // fall through to no authorization |
| 163 | Ok(Self::with_authorization(Vec::new(), Vec::new())) |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /// Returns the buffer that needs to be filled with incoming data from the server. |
| 169 | /// |
| 170 | /// After filling this buffer (using a method like `Read::read`), call [`Self::advance`] with |
| 171 | /// the number of bytes read to indicate that the buffer has been filled. |
| 172 | pub fn buffer(&mut self) -> &mut [u8] { |
| 173 | &mut self.buffer[self.advanced..] |
| 174 | } |
| 175 | |
| 176 | /// Advance the internal buffer, given the number of bytes that have been read. |
| 177 | pub fn advance(&mut self, bytes: usize) -> bool { |
| 178 | self.advanced += bytes; |
| 179 | debug_assert!(self.buffer.len() >= self.advanced); |
| 180 | |
| 181 | // if we've read up to the initial capacity, tell how many more bytes |
| 182 | // we need to read |
| 183 | if self.advanced == INITIAL_CAPACITY { |
| 184 | // remaining length is at byte range 6-7 in 4-bytes |
| 185 | let length = u16::from_ne_bytes([self.buffer[6], self.buffer[7]]); |
| 186 | let length = length as usize * 4; |
| 187 | |
| 188 | // allocate more room |
| 189 | // use reserve_exact because this will be the final |
| 190 | // length of the vector |
| 191 | self.buffer.reserve_exact(length); |
| 192 | self.buffer.resize(length + self.buffer.len(), 0); |
| 193 | false |
| 194 | } else { |
| 195 | self.advanced == self.buffer.len() |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | /// Returns the setup provided by the server. |
| 200 | /// |
| 201 | /// # Errors |
| 202 | /// |
| 203 | /// - If this method is called before the server returns all of the required data, |
| 204 | /// it returns `ConnectError::NotEnoughData`. |
| 205 | /// - If the server fails to establish the X11 connection, the `ConnectError::SetupFailed` |
| 206 | /// variant is returned. |
| 207 | /// - If the server failed to authenticate the user, the `ConnectError::SetupAuthenticate` |
| 208 | /// error is returned. |
| 209 | /// - If the server failed to parse any of the above responses, the |
| 210 | /// `ConnectError::ParseError` error is returned. |
| 211 | pub fn into_setup(self) -> Result<Setup, ConnectError> { |
| 212 | // if we aren't full yet, panic |
| 213 | if self.advanced != self.buffer.len() { |
| 214 | return Err(ConnectError::Incomplete { |
| 215 | expected: self.buffer.len(), |
| 216 | received: self.advanced, |
| 217 | }); |
| 218 | } |
| 219 | |
| 220 | // parse the setup response |
| 221 | match self.buffer[0] { |
| 222 | 0 => { |
| 223 | // an error has occurred |
| 224 | let (failed, _) = SetupFailed::try_parse(&self.buffer)?; |
| 225 | Err(ConnectError::SetupFailed(failed)) |
| 226 | } |
| 227 | 1 => { |
| 228 | // the setup is valid! |
| 229 | let (success, _) = Setup::try_parse(&self.buffer)?; |
| 230 | Ok(success) |
| 231 | } |
| 232 | 2 => { |
| 233 | // we need further authentication |
| 234 | let (more_auth, _) = SetupAuthenticate::try_parse(&self.buffer)?; |
| 235 | Err(ConnectError::SetupAuthenticate(more_auth)) |
| 236 | } |
| 237 | _ => { |
| 238 | // this is undefined |
| 239 | Err(ParseError::InvalidValue.into()) |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | impl fmt::Debug for Connect { |
| 246 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 247 | f&mut DebugStruct<'_, '_>.debug_struct("Connect" ) |
| 248 | .field( |
| 249 | name:"buffer" , |
| 250 | &format_args!(" {}/ {}" , self.advanced, self.buffer.len()), |
| 251 | ) |
| 252 | .finish() |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | impl TryFrom<Connect> for Setup { |
| 257 | type Error = ConnectError; |
| 258 | |
| 259 | fn try_from(connect: Connect) -> Result<Self, Self::Error> { |
| 260 | connect.into_setup() |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | #[cfg (test)] |
| 265 | #[cfg (feature = "extra-traits" )] |
| 266 | mod tests { |
| 267 | use super::Connect; |
| 268 | use crate::errors::ConnectError; |
| 269 | use crate::protocol::xproto::{ImageOrder, Setup, SetupAuthenticate, SetupFailed}; |
| 270 | use crate::x11_utils::Serialize; |
| 271 | use alloc::vec; |
| 272 | |
| 273 | fn test_setup() -> Setup { |
| 274 | let mut s = Setup { |
| 275 | status: 1, |
| 276 | protocol_major_version: 11, |
| 277 | protocol_minor_version: 0, |
| 278 | length: 0, |
| 279 | release_number: 0, |
| 280 | resource_id_base: 1, |
| 281 | resource_id_mask: 1, |
| 282 | motion_buffer_size: 0, |
| 283 | maximum_request_length: 0, |
| 284 | image_byte_order: ImageOrder::LSB_FIRST, |
| 285 | bitmap_format_bit_order: ImageOrder::LSB_FIRST, |
| 286 | bitmap_format_scanline_unit: 32, |
| 287 | bitmap_format_scanline_pad: 32, |
| 288 | min_keycode: 0, |
| 289 | max_keycode: 0, |
| 290 | vendor: b"Testing Setup" .to_vec(), |
| 291 | pixmap_formats: vec![], |
| 292 | roots: vec![], |
| 293 | }; |
| 294 | // +3 so it rounds up |
| 295 | s.length = ((s.serialize().len() - 8 + 3) / 4) as u16; |
| 296 | s |
| 297 | } |
| 298 | |
| 299 | fn try_receive_bytes(item: &impl Serialize) -> Result<Setup, ConnectError> { |
| 300 | let mut connect = Connect::blank(); |
| 301 | |
| 302 | // feed in a setup |
| 303 | let mut item_bytes = vec![]; |
| 304 | item.serialize_into(&mut item_bytes); |
| 305 | |
| 306 | let mut i = 0; |
| 307 | loop { |
| 308 | i += 1; |
| 309 | if i > 500 { |
| 310 | panic!("too many iterations" ); |
| 311 | } |
| 312 | |
| 313 | // copy bytes to connect |
| 314 | let buffer = connect.buffer(); |
| 315 | let bytes_to_copy = std::cmp::min(item_bytes.len(), buffer.len()); |
| 316 | buffer[..bytes_to_copy].copy_from_slice(&item_bytes[..bytes_to_copy]); |
| 317 | |
| 318 | // drain the bytes that we've already copied |
| 319 | drop(item_bytes.drain(..bytes_to_copy)); |
| 320 | |
| 321 | // check advance |
| 322 | if connect.advance(bytes_to_copy) { |
| 323 | break; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | connect.into_setup() |
| 328 | } |
| 329 | |
| 330 | #[test ] |
| 331 | fn test_connect_receive_setup() { |
| 332 | let setup = test_setup(); |
| 333 | let b = try_receive_bytes(&setup); |
| 334 | |
| 335 | match b { |
| 336 | Ok(s) => assert_eq!(s, setup), |
| 337 | Err(e) => panic!("{:?}" , e), |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | #[test ] |
| 342 | fn test_connect_receive_setup_authenticate() { |
| 343 | let setup = SetupAuthenticate { |
| 344 | status: 2, |
| 345 | reason: b"Needs more auth." .to_vec(), |
| 346 | }; |
| 347 | |
| 348 | let b = try_receive_bytes(&setup); |
| 349 | match b { |
| 350 | Ok(s) => panic!("{:?}" , s), |
| 351 | Err(ConnectError::SetupAuthenticate(e)) => assert_eq!(e, setup), |
| 352 | Err(e) => panic!("{:?}" , e), |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | #[test ] |
| 357 | fn test_connect_receive_setup_failed() { |
| 358 | let mut setup = SetupFailed { |
| 359 | status: 0, |
| 360 | protocol_major_version: 11, |
| 361 | protocol_minor_version: 0, |
| 362 | length: 0, |
| 363 | reason: b"whatever" .to_vec(), |
| 364 | }; |
| 365 | setup.length = ((setup.serialize().len() - 8) / 4) as _; |
| 366 | |
| 367 | let b = try_receive_bytes(&setup); |
| 368 | match b { |
| 369 | Ok(s) => panic!("{:?}" , s), |
| 370 | Err(ConnectError::SetupFailed(e)) => assert_eq!(e, setup), |
| 371 | Err(e) => panic!("{:?}" , e), |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | |