| 1 | //! Helper types for implementing an X11 client. |
| 2 | |
| 3 | use alloc::collections::VecDeque; |
| 4 | use alloc::vec::Vec; |
| 5 | |
| 6 | use crate::utils::RawFdContainer; |
| 7 | use crate::{DiscardMode, SequenceNumber}; |
| 8 | |
| 9 | /// A combination of a buffer and a list of file descriptors. |
| 10 | pub type BufWithFds = crate::BufWithFds<Vec<u8>>; |
| 11 | |
| 12 | /// The raw bytes of an X11 event and its sequence number. |
| 13 | pub type RawEventAndSeqNumber = crate::RawEventAndSeqNumber<Vec<u8>>; |
| 14 | |
| 15 | /// Information about the reply to an X11 request. |
| 16 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
| 17 | pub enum ReplyFdKind { |
| 18 | /// The request does not have a reply. |
| 19 | NoReply, |
| 20 | /// The request has a reply and that reply does *not* contain any file descriptors. |
| 21 | ReplyWithoutFDs, |
| 22 | /// The request has a reply and that reply *does* contain file descriptor(s). |
| 23 | ReplyWithFDs, |
| 24 | } |
| 25 | |
| 26 | /// Information about the result of polling for a reply packet. |
| 27 | #[derive (Debug, Clone)] |
| 28 | pub enum PollReply { |
| 29 | /// It is not clear yet what the result will be; try again later. |
| 30 | TryAgain, |
| 31 | /// There will be no reply; polling is done. |
| 32 | NoReply, |
| 33 | /// Here is the result of the polling; polling is done. |
| 34 | Reply(Vec<u8>), |
| 35 | } |
| 36 | |
| 37 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
| 38 | struct SentRequest { |
| 39 | seqno: SequenceNumber, |
| 40 | discard_mode: Option<DiscardMode>, |
| 41 | has_fds: bool, |
| 42 | } |
| 43 | |
| 44 | /// A pure-rust, sans-I/O implementation of the X11 protocol. |
| 45 | /// |
| 46 | /// This object is designed to be used in combination with an I/O backend, in |
| 47 | /// order to keep state for the X11 protocol. |
| 48 | #[derive (Debug)] |
| 49 | pub struct Connection { |
| 50 | // The sequence number of the last request that was written |
| 51 | last_sequence_written: SequenceNumber, |
| 52 | // Sorted(!) list with information on requests that were written, but no answer received yet. |
| 53 | sent_requests: VecDeque<SentRequest>, |
| 54 | |
| 55 | // The sequence number of the next reply that is expected to come in |
| 56 | next_reply_expected: SequenceNumber, |
| 57 | |
| 58 | // The sequence number of the last reply/error/event that was read |
| 59 | last_sequence_read: SequenceNumber, |
| 60 | // Events that were read, but not yet returned to the API user |
| 61 | pending_events: VecDeque<(SequenceNumber, Vec<u8>)>, |
| 62 | // Replies that were read, but not yet returned to the API user |
| 63 | pending_replies: VecDeque<(SequenceNumber, BufWithFds)>, |
| 64 | |
| 65 | // FDs that were read, but not yet assigned to any reply |
| 66 | pending_fds: VecDeque<RawFdContainer>, |
| 67 | } |
| 68 | |
| 69 | impl Default for Connection { |
| 70 | fn default() -> Self { |
| 71 | Self::new() |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | impl Connection { |
| 76 | /// Create a new `Connection`. |
| 77 | /// |
| 78 | /// It is assumed that the connection was just established. This means that the next request |
| 79 | /// that is sent will have sequence number one. |
| 80 | pub fn new() -> Self { |
| 81 | Connection { |
| 82 | last_sequence_written: 0, |
| 83 | next_reply_expected: 0, |
| 84 | last_sequence_read: 0, |
| 85 | sent_requests: VecDeque::new(), |
| 86 | pending_events: VecDeque::new(), |
| 87 | pending_replies: VecDeque::new(), |
| 88 | pending_fds: VecDeque::new(), |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /// Send a request to the X11 server. |
| 93 | /// |
| 94 | /// When this returns `None`, a sync with the server is necessary. Afterwards, the caller |
| 95 | /// should try again. |
| 96 | pub fn send_request(&mut self, kind: ReplyFdKind) -> Option<SequenceNumber> { |
| 97 | let has_response = match kind { |
| 98 | ReplyFdKind::NoReply => false, |
| 99 | ReplyFdKind::ReplyWithoutFDs => true, |
| 100 | ReplyFdKind::ReplyWithFDs => true, |
| 101 | }; |
| 102 | if self.next_reply_expected + SequenceNumber::from(u16::max_value()) - 1 |
| 103 | <= self.last_sequence_written |
| 104 | && !has_response |
| 105 | { |
| 106 | // The caller need to call send_sync(). Otherwise, we might not be able to reconstruct |
| 107 | // full sequence numbers for received packets. |
| 108 | return None; |
| 109 | } |
| 110 | |
| 111 | self.last_sequence_written += 1; |
| 112 | let seqno = self.last_sequence_written; |
| 113 | |
| 114 | if has_response { |
| 115 | self.next_reply_expected = self.last_sequence_written; |
| 116 | } |
| 117 | |
| 118 | let sent_request = SentRequest { |
| 119 | seqno, |
| 120 | discard_mode: None, |
| 121 | has_fds: kind == ReplyFdKind::ReplyWithFDs, |
| 122 | }; |
| 123 | self.sent_requests.push_back(sent_request); |
| 124 | |
| 125 | Some(seqno) |
| 126 | } |
| 127 | |
| 128 | /// Ignore the reply for a request that was previously sent. |
| 129 | pub fn discard_reply(&mut self, seqno: SequenceNumber, mode: DiscardMode) { |
| 130 | if let Ok(index) = self.sent_requests.binary_search_by_key(&seqno, |r| r.seqno) { |
| 131 | self.sent_requests[index].discard_mode = Some(mode); |
| 132 | } |
| 133 | match mode { |
| 134 | DiscardMode::DiscardReplyAndError => self.pending_replies.retain(|r| r.0 != seqno), |
| 135 | DiscardMode::DiscardReply => { |
| 136 | if let Some(index) = self.pending_replies.iter().position(|r| r.0 == seqno) { |
| 137 | while self |
| 138 | .pending_replies |
| 139 | .get(index) |
| 140 | .filter(|r| r.0 == seqno) |
| 141 | .is_some() |
| 142 | { |
| 143 | if let Some((_, packet)) = self.pending_replies.remove(index) { |
| 144 | if packet.0[0] == 0 { |
| 145 | // This is an error |
| 146 | self.pending_events.push_back((seqno, packet.0)); |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | // Extract the sequence number from a packet read from the X11 server. The packet must be a |
| 156 | // reply, an event, or an error. All of these have a u16 sequence number in bytes 2 and 3... |
| 157 | // except for KeymapNotify events. |
| 158 | fn extract_sequence_number(&mut self, buffer: &[u8]) -> Option<SequenceNumber> { |
| 159 | use crate::protocol::xproto::KEYMAP_NOTIFY_EVENT; |
| 160 | if buffer[0] == KEYMAP_NOTIFY_EVENT { |
| 161 | return None; |
| 162 | } |
| 163 | // We get the u16 from the wire... |
| 164 | let number = u16::from_ne_bytes([buffer[2], buffer[3]]); |
| 165 | |
| 166 | // ...and use our state to reconstruct the high bytes |
| 167 | let high_bytes = self.last_sequence_read & !SequenceNumber::from(u16::max_value()); |
| 168 | let mut full_number = SequenceNumber::from(number) | high_bytes; |
| 169 | if full_number < self.last_sequence_read { |
| 170 | full_number += SequenceNumber::from(u16::max_value()) + 1; |
| 171 | } |
| 172 | |
| 173 | // Update our state |
| 174 | self.last_sequence_read = full_number; |
| 175 | if self.next_reply_expected < full_number { |
| 176 | // This is most likely an event/error that allows us to update our sequence number |
| 177 | // implicitly. Normally, only requests with a reply update this (in send_request()). |
| 178 | self.next_reply_expected = full_number; |
| 179 | } |
| 180 | Some(full_number) |
| 181 | } |
| 182 | |
| 183 | /// Add FDs that were received to the internal state. |
| 184 | /// |
| 185 | /// This must be called before the corresponding packets are enqueued. |
| 186 | pub fn enqueue_fds(&mut self, fds: Vec<RawFdContainer>) { |
| 187 | self.pending_fds.extend(fds); |
| 188 | } |
| 189 | |
| 190 | /// An X11 packet was received from the connection and is now enqueued into our state. |
| 191 | /// |
| 192 | /// Any FDs that were received must already be enqueued before this can be called. |
| 193 | pub fn enqueue_packet(&mut self, packet: Vec<u8>) { |
| 194 | let kind = packet[0]; |
| 195 | |
| 196 | // extract_sequence_number() updates our state and is thus important to call even when we |
| 197 | // do not need the sequence number |
| 198 | let seqno = self |
| 199 | .extract_sequence_number(&packet) |
| 200 | .unwrap_or(self.last_sequence_read); |
| 201 | |
| 202 | // Remove all entries for older requests |
| 203 | while let Some(request) = self.sent_requests.front() { |
| 204 | if request.seqno >= seqno { |
| 205 | break; |
| 206 | } |
| 207 | let _ = self.sent_requests.pop_front(); |
| 208 | } |
| 209 | let request = self.sent_requests.front().filter(|r| r.seqno == seqno); |
| 210 | |
| 211 | if kind == 0 { |
| 212 | // It is an error. Let's see where we have to send it to. |
| 213 | if let Some(request) = request { |
| 214 | match request.discard_mode { |
| 215 | Some(DiscardMode::DiscardReplyAndError) => { /* This error should be ignored */ |
| 216 | } |
| 217 | Some(DiscardMode::DiscardReply) => { |
| 218 | self.pending_events.push_back((seqno, packet)) |
| 219 | } |
| 220 | None => self |
| 221 | .pending_replies |
| 222 | .push_back((seqno, (packet, Vec::new()))), |
| 223 | } |
| 224 | } else { |
| 225 | // Unexpected error, send to main loop |
| 226 | self.pending_events.push_back((seqno, packet)); |
| 227 | } |
| 228 | } else if kind == 1 { |
| 229 | let fds = if request.filter(|r| r.has_fds).is_some() { |
| 230 | // This reply has FDs, the number of FDs is always in the second byte |
| 231 | let num_fds = usize::from(packet[1]); |
| 232 | // FIXME Turn this into some kind of "permanent error state" (so that |
| 233 | // everything fails with said error) instead of using a panic (this panic will |
| 234 | // likely poison some Mutex and produce an error state that way). |
| 235 | assert!( |
| 236 | num_fds <= self.pending_fds.len(), |
| 237 | "FIXME: The server sent us too few FDs. The connection is now unusable \ |
| 238 | since we will never be sure again which FD belongs to which reply." |
| 239 | ); |
| 240 | self.pending_fds.drain(..num_fds).collect() |
| 241 | } else { |
| 242 | Vec::new() |
| 243 | }; |
| 244 | |
| 245 | // It is a reply |
| 246 | if request.filter(|r| r.discard_mode.is_some()).is_some() { |
| 247 | // This reply should be discarded |
| 248 | } else { |
| 249 | self.pending_replies.push_back((seqno, (packet, fds))); |
| 250 | } |
| 251 | } else { |
| 252 | // It is an event |
| 253 | self.pending_events.push_back((seqno, packet)); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | /// Check if the server already sent an answer to the request with the given sequence number. |
| 258 | /// |
| 259 | /// This function is meant to be used for requests that have a reply. Such requests always |
| 260 | /// cause a reply or an error to be sent. |
| 261 | pub fn poll_for_reply_or_error(&mut self, sequence: SequenceNumber) -> Option<BufWithFds> { |
| 262 | for (index, (seqno, _packet)) in self.pending_replies.iter().enumerate() { |
| 263 | if *seqno == sequence { |
| 264 | return Some(self.pending_replies.remove(index).unwrap().1); |
| 265 | } |
| 266 | } |
| 267 | None |
| 268 | } |
| 269 | |
| 270 | /// Prepare for calling `poll_check_for_reply_or_error()`. |
| 271 | /// |
| 272 | /// To check if a request with a reply caused an error, one simply has to wait for the error or |
| 273 | /// reply to be received. However, this approach does not work for requests without errors: |
| 274 | /// Success is indicated by the absence of an error. |
| 275 | /// |
| 276 | /// Thus, this function returns true if a sync is necessary to ensure that a reply with a |
| 277 | /// higher sequence number will be received. Since the X11 server handles requests in-order, |
| 278 | /// if the reply to a later request is received, this means that the earlier request did not |
| 279 | /// fail. |
| 280 | pub fn prepare_check_for_reply_or_error(&mut self, sequence: SequenceNumber) -> bool { |
| 281 | self.next_reply_expected < sequence |
| 282 | } |
| 283 | |
| 284 | /// Check if the request with the given sequence number was already handled by the server. |
| 285 | /// |
| 286 | /// Before calling this function, you must call `prepare_check_for_reply_or_error()` with the |
| 287 | /// sequence number. |
| 288 | /// |
| 289 | /// This function can be used for requests with and without a reply. |
| 290 | pub fn poll_check_for_reply_or_error(&mut self, sequence: SequenceNumber) -> PollReply { |
| 291 | if let Some(result) = self.poll_for_reply_or_error(sequence) { |
| 292 | return PollReply::Reply(result.0); |
| 293 | } |
| 294 | |
| 295 | if self.last_sequence_read > sequence { |
| 296 | // We can be sure that there will be no reply/error |
| 297 | PollReply::NoReply |
| 298 | } else { |
| 299 | // Hm, we cannot be sure yet. Perhaps there will still be a reply/error |
| 300 | PollReply::TryAgain |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /// Find the reply for the request with the given sequence number. |
| 305 | /// |
| 306 | /// If the request caused an error, that error will be handled as an event. This means that a |
| 307 | /// latter call to `poll_for_event()` will return it. |
| 308 | pub fn poll_for_reply(&mut self, sequence: SequenceNumber) -> PollReply { |
| 309 | if let Some(reply) = self.poll_for_reply_or_error(sequence) { |
| 310 | if reply.0[0] == 0 { |
| 311 | self.pending_events.push_back((sequence, reply.0)); |
| 312 | PollReply::NoReply |
| 313 | } else { |
| 314 | PollReply::Reply(reply.0) |
| 315 | } |
| 316 | } else { |
| 317 | PollReply::TryAgain |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | /// Get a pending event. |
| 322 | pub fn poll_for_event_with_sequence(&mut self) -> Option<RawEventAndSeqNumber> { |
| 323 | self.pending_events |
| 324 | .pop_front() |
| 325 | .map(|(seqno, event)| (event, seqno)) |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | #[cfg (test)] |
| 330 | mod test { |
| 331 | use super::{Connection, ReplyFdKind}; |
| 332 | |
| 333 | #[test ] |
| 334 | fn insert_sync_no_reply() { |
| 335 | // The connection must send a sync (GetInputFocus) request every 2^16-1 requests (that do not |
| 336 | // have a reply). Thus, this test sends more than that and tests for the sync to appear. |
| 337 | |
| 338 | let mut connection = Connection::new(); |
| 339 | |
| 340 | for num in 1..0xffff { |
| 341 | let seqno = connection.send_request(ReplyFdKind::NoReply); |
| 342 | assert_eq!(Some(num), seqno); |
| 343 | } |
| 344 | // request 0xffff should be a sync, hence the next one is 0x10000 |
| 345 | let seqno = connection.send_request(ReplyFdKind::NoReply); |
| 346 | assert_eq!(None, seqno); |
| 347 | |
| 348 | let seqno = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 349 | assert_eq!(Some(0xffff), seqno); |
| 350 | |
| 351 | let seqno = connection.send_request(ReplyFdKind::NoReply); |
| 352 | assert_eq!(Some(0x10000), seqno); |
| 353 | } |
| 354 | |
| 355 | #[test ] |
| 356 | fn insert_no_sync_with_reply() { |
| 357 | // Compared to the previous test, this uses ReplyFdKind::ReplyWithoutFDs, so no sync needs to |
| 358 | // be inserted. |
| 359 | |
| 360 | let mut connection = Connection::new(); |
| 361 | |
| 362 | for num in 1..=0x10001 { |
| 363 | let seqno = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 364 | assert_eq!(Some(num), seqno); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | #[test ] |
| 369 | fn insert_no_sync_when_already_syncing() { |
| 370 | // This test sends enough ReplyFdKind::NoReply requests that a sync becomes necessary on |
| 371 | // the next request. Then it sends a ReplyFdKind::ReplyWithoutFDs request so that no sync is |
| 372 | // necessary. This is a regression test: Once upon a time, an unnecessary sync was done. |
| 373 | |
| 374 | let mut connection = Connection::new(); |
| 375 | |
| 376 | for num in 1..0xffff { |
| 377 | let seqno = connection.send_request(ReplyFdKind::NoReply); |
| 378 | assert_eq!(Some(num), seqno); |
| 379 | } |
| 380 | |
| 381 | let seqno = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 382 | assert_eq!(Some(0xffff), seqno); |
| 383 | } |
| 384 | |
| 385 | #[test ] |
| 386 | fn get_sync_replies() { |
| 387 | // This sends requests with a reply with seqno 1 and 1+2^16 and then checks that their |
| 388 | // replies are correctly mapped to the requests. |
| 389 | |
| 390 | let mut connection = Connection::new(); |
| 391 | |
| 392 | let first_reply = 1; |
| 393 | let second_reply = 0x10001; |
| 394 | |
| 395 | // First, send all the requests |
| 396 | |
| 397 | // First request is one with a reply |
| 398 | let seqno = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 399 | assert_eq!(Some(first_reply), seqno); |
| 400 | |
| 401 | // Then, there should be enough requests so that the next request will end up with sequence |
| 402 | // number 'second_reply' |
| 403 | for num in (first_reply + 1)..(second_reply - 1) { |
| 404 | let seqno = connection.send_request(ReplyFdKind::NoReply); |
| 405 | assert_eq!(Some(num), seqno); |
| 406 | } |
| 407 | |
| 408 | // Send one more request. This needs to be a sync request so that sequence numbers can be |
| 409 | // reconstructed correctly. The bug that we testing was that no sync was required, so this |
| 410 | // test handles both cases correctly. |
| 411 | let requested_extra_sync = connection.send_request(ReplyFdKind::NoReply).is_none(); |
| 412 | if requested_extra_sync { |
| 413 | let _ = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 414 | } |
| 415 | |
| 416 | let seqno = connection.send_request(ReplyFdKind::ReplyWithoutFDs); |
| 417 | assert_eq!(Some(second_reply), seqno); |
| 418 | |
| 419 | // Prepare a reply packet |
| 420 | let mut packet = [0; 32]; |
| 421 | // It is a reply |
| 422 | packet[0] = 1; |
| 423 | // Set the sequence number to 1 |
| 424 | packet[2..4].copy_from_slice(&1u16.to_ne_bytes()); |
| 425 | |
| 426 | // Enqueue the first reply. |
| 427 | connection.enqueue_packet(packet.to_vec()); |
| 428 | |
| 429 | // Send an extra reply if the code wanted one. This extra reply allows to detect that all |
| 430 | // replies to the first request were received (remember, there can be multiple replies to a |
| 431 | // single request!) |
| 432 | if requested_extra_sync { |
| 433 | packet[2..4].copy_from_slice(&((second_reply - 1) as u16).to_ne_bytes()); |
| 434 | connection.enqueue_packet(packet.to_vec()); |
| 435 | } |
| 436 | |
| 437 | // Set the sequence number for the second reply |
| 438 | packet[2..4].copy_from_slice(&(second_reply as u16).to_ne_bytes()); |
| 439 | connection.enqueue_packet(packet.to_vec()); |
| 440 | |
| 441 | // Now check that the sequence number for the last packet was reconstructed correctly. |
| 442 | assert!(connection.poll_for_reply_or_error(second_reply).is_some()); |
| 443 | } |
| 444 | } |
| 445 | |