Skip to main content

fips_core/transport/tcp/
stream.rs

1//! FIPS Stream Reader
2//!
3//! Recovers FIPS packet boundaries from a TCP byte stream using the
4//! shared 4-byte FMP/FSP prefix `[ver+phase:1][flags:1][payload_len:2 LE]`.
5//!
6//! This module is deliberately separate from the TCP transport so it can
7//! be reused by the future Tor transport.
8
9use std::time::Duration;
10
11use tokio::io::{AsyncRead, AsyncReadExt};
12use tokio::time::timeout;
13
14use crate::proto::fsp_wire::{FSP_FLAG_CP, FSP_FLAG_DIRECT_TRANSPORT, FSP_FLAG_U, FSP_HEADER_SIZE};
15
16/// FMP phase values (low nibble of byte 0).
17const PHASE_ESTABLISHED: u8 = 0x0;
18const PHASE_MSG1: u8 = 0x1;
19const PHASE_MSG2: u8 = 0x2;
20
21/// Size of the FMP common prefix.
22const PREFIX_SIZE: usize = 4;
23
24/// Overhead for established frames: 12 bytes remaining header + 16 bytes AEAD tag.
25/// The full established header is 16 bytes (PREFIX_SIZE + 12), so after reading
26/// the 4-byte prefix, 12 more header bytes remain. Then payload_len bytes of
27/// ciphertext, then 16 bytes of AEAD tag.
28const FMP_ESTABLISHED_REMAINING_HEADER: usize = 12;
29const DIRECT_FSP_REMAINING_HEADER: usize = FSP_HEADER_SIZE - PREFIX_SIZE;
30const AEAD_TAG_SIZE: usize = 16;
31
32/// Maximum time allowed to finish a byte-stream record after its first byte.
33/// Waiting for that first byte remains unbounded on established idle links.
34pub(crate) const DEFAULT_FRAME_COMPLETION_TIMEOUT: Duration = Duration::from_secs(30);
35
36/// Errors from the FMP stream reader.
37#[derive(Debug)]
38pub enum StreamError {
39    /// Unknown FMP version — not a FIPS connection (e.g., TLS ClientHello).
40    UnknownVersion(u8),
41    /// Unknown FMP phase byte — protocol error, close connection.
42    UnknownPhase(u8),
43    /// Payload length exceeds the connection's MTU — corrupted or malicious.
44    PayloadTooLarge {
45        payload_len: u16,
46        max_payload_len: u16,
47    },
48    /// Handshake packet has unexpected payload_len for its phase.
49    HandshakeSizeMismatch { phase: u8, expected: u16, got: u16 },
50    /// I/O error (including EOF).
51    Io(std::io::Error),
52}
53
54impl std::fmt::Display for StreamError {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            StreamError::UnknownVersion(v) => write!(f, "unknown FMP version: {}", v),
58            StreamError::UnknownPhase(p) => write!(f, "unknown FMP phase: 0x{:02x}", p),
59            StreamError::PayloadTooLarge {
60                payload_len,
61                max_payload_len,
62            } => {
63                write!(
64                    f,
65                    "payload_len {} exceeds max {}",
66                    payload_len, max_payload_len
67                )
68            }
69            StreamError::HandshakeSizeMismatch {
70                phase,
71                expected,
72                got,
73            } => {
74                write!(
75                    f,
76                    "handshake phase 0x{:x}: expected payload_len {}, got {}",
77                    phase, expected, got
78                )
79            }
80            StreamError::Io(e) => write!(f, "io: {}", e),
81        }
82    }
83}
84
85impl std::error::Error for StreamError {}
86
87impl From<std::io::Error> for StreamError {
88    fn from(e: std::io::Error) -> Self {
89        StreamError::Io(e)
90    }
91}
92
93/// Known wire sizes for handshake messages.
94/// msg1: 4 (prefix) + 4 (sender_idx) + 106 (noise_msg1) = 114 bytes
95/// msg2: 4 (prefix) + 4 (sender_idx) + 4 (receiver_idx) + 57 (noise_msg2) = 69 bytes
96const MSG1_WIRE_SIZE: usize = 114;
97const MSG2_WIRE_SIZE: usize = 69;
98
99/// Expected payload_len for msg1: sender_idx(4) + noise_msg1(106) = 110.
100const MSG1_PAYLOAD_LEN: u16 = (MSG1_WIRE_SIZE - PREFIX_SIZE) as u16;
101
102/// Expected payload_len for msg2: sender_idx(4) + receiver_idx(4) + noise_msg2(57) = 65.
103const MSG2_PAYLOAD_LEN: u16 = (MSG2_WIRE_SIZE - PREFIX_SIZE) as u16;
104
105fn invalid_data(message: impl Into<String>) -> StreamError {
106    StreamError::Io(std::io::Error::new(
107        std::io::ErrorKind::InvalidData,
108        message.into(),
109    ))
110}
111
112fn completion_timed_out() -> StreamError {
113    StreamError::Io(std::io::Error::new(
114        std::io::ErrorKind::TimedOut,
115        "stream frame completion timed out",
116    ))
117}
118
119fn stream_record_len(prefix: &[u8; PREFIX_SIZE]) -> Result<usize, StreamError> {
120    let version = prefix[0] >> 4;
121    let phase = prefix[0] & 0x0F;
122
123    if version != 0 {
124        return Err(StreamError::UnknownVersion(version));
125    }
126
127    let payload_len = u16::from_le_bytes([prefix[2], prefix[3]]);
128
129    let remaining = match phase {
130        PHASE_ESTABLISHED => {
131            let is_direct_fsp = prefix[1] & FSP_FLAG_DIRECT_TRANSPORT != 0;
132            if is_direct_fsp && prefix[1] & (FSP_FLAG_CP | FSP_FLAG_U) != 0 {
133                return Err(invalid_data(format!(
134                    "invalid direct FSP flags: 0x{:02x}",
135                    prefix[1]
136                )));
137            }
138            let remaining_header = if is_direct_fsp {
139                DIRECT_FSP_REMAINING_HEADER
140            } else {
141                FMP_ESTABLISHED_REMAINING_HEADER
142            };
143            remaining_header + payload_len as usize + AEAD_TAG_SIZE
144        }
145        PHASE_MSG1 => {
146            if payload_len != MSG1_PAYLOAD_LEN {
147                return Err(StreamError::HandshakeSizeMismatch {
148                    phase,
149                    expected: MSG1_PAYLOAD_LEN,
150                    got: payload_len,
151                });
152            }
153            payload_len as usize
154        }
155        PHASE_MSG2 => {
156            if payload_len != MSG2_PAYLOAD_LEN {
157                return Err(StreamError::HandshakeSizeMismatch {
158                    phase,
159                    expected: MSG2_PAYLOAD_LEN,
160                    got: payload_len,
161                });
162            }
163            payload_len as usize
164        }
165        _ => {
166            return Err(StreamError::UnknownPhase(phase));
167        }
168    };
169
170    Ok(PREFIX_SIZE + remaining)
171}
172
173/// Validate that one byte slice is exactly one FIPS byte-stream record.
174pub(crate) fn validate_stream_record(data: &[u8]) -> Result<(), StreamError> {
175    let prefix: [u8; PREFIX_SIZE] = data
176        .get(..PREFIX_SIZE)
177        .ok_or_else(|| {
178            invalid_data(format!(
179                "record size mismatch: expected at least {PREFIX_SIZE}, got {}",
180                data.len()
181            ))
182        })?
183        .try_into()
184        .expect("four-byte prefix slice");
185    let expected = stream_record_len(&prefix)?;
186    if data.len() != expected {
187        return Err(invalid_data(format!(
188            "record size mismatch: expected {expected}, got {}",
189            data.len()
190        )));
191    }
192    Ok(())
193}
194
195/// Read one complete FIPS packet from an async reader.
196///
197/// This preserves the public v0.4.4 API and its MTU validation semantics for
198/// external callers. Transport runtimes use the private timed reader below;
199/// their byte-stream record limit is the protocol's u16 payload field rather
200/// than the advertised path MTU.
201pub async fn read_fmp_packet<R: AsyncRead + Unpin>(
202    reader: &mut R,
203    mtu: u16,
204) -> Result<Vec<u8>, StreamError> {
205    let mut prefix = [0u8; PREFIX_SIZE];
206    reader.read_exact(&mut prefix).await?;
207    let total = stream_record_len(&prefix)?;
208
209    if prefix[0] & 0x0f == PHASE_ESTABLISHED {
210        let payload_len = u16::from_le_bytes([prefix[2], prefix[3]]);
211        let remaining_header = if prefix[1] & FSP_FLAG_DIRECT_TRANSPORT != 0 {
212            DIRECT_FSP_REMAINING_HEADER
213        } else {
214            FMP_ESTABLISHED_REMAINING_HEADER
215        };
216        let max_payload_len =
217            mtu.saturating_sub((PREFIX_SIZE + remaining_header + AEAD_TAG_SIZE) as u16);
218        if payload_len > max_payload_len {
219            return Err(StreamError::PayloadTooLarge {
220                payload_len,
221                max_payload_len,
222            });
223        }
224    }
225
226    let mut packet = vec![0u8; total];
227    packet[..PREFIX_SIZE].copy_from_slice(&prefix);
228    reader.read_exact(&mut packet[PREFIX_SIZE..]).await?;
229    Ok(packet)
230}
231
232/// Read one complete FIPS packet for a transport byte stream.
233///
234/// An established idle link may wait indefinitely for the first record byte.
235/// Once any byte arrives, the rest of the prefix and body share one absolute
236/// completion deadline, preventing partial-prefix and partial-body stalls.
237pub(crate) async fn read_fmp_packet_with_timeout<R: AsyncRead + Unpin>(
238    reader: &mut R,
239    completion_timeout: Duration,
240) -> Result<Vec<u8>, StreamError> {
241    let mut prefix = [0u8; PREFIX_SIZE];
242    reader.read_exact(&mut prefix[..1]).await?;
243
244    timeout(completion_timeout, async {
245        reader.read_exact(&mut prefix[1..]).await?;
246        let total = stream_record_len(&prefix)?;
247        let mut packet = vec![0u8; total];
248        packet[..PREFIX_SIZE].copy_from_slice(&prefix);
249        reader.read_exact(&mut packet[PREFIX_SIZE..]).await?;
250        Ok::<_, StreamError>(packet)
251    })
252    .await
253    .map_err(|_| completion_timed_out())?
254}
255
256// ============================================================================
257// Tests
258// ============================================================================
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use std::io::Cursor;
264
265    const READ_TIMEOUT: Duration = Duration::from_secs(1);
266
267    async fn read_fmp_packet<R: AsyncRead + Unpin>(
268        reader: &mut R,
269        completion_timeout: Duration,
270    ) -> Result<Vec<u8>, StreamError> {
271        read_fmp_packet_with_timeout(reader, completion_timeout).await
272    }
273
274    /// Build a minimal established frame with the given payload_len.
275    /// Layout: [ver+phase:1][flags:1][payload_len:2 LE][12 bytes header][payload_len bytes][16 bytes tag]
276    fn build_established_frame(payload_len: u16) -> Vec<u8> {
277        let total =
278            PREFIX_SIZE + FMP_ESTABLISHED_REMAINING_HEADER + payload_len as usize + AEAD_TAG_SIZE;
279        let mut frame = vec![0u8; total];
280        frame[0] = 0x00; // ver=0, phase=0 (established)
281        frame[1] = 0x00; // flags
282        frame[2..4].copy_from_slice(&payload_len.to_le_bytes());
283        // Fill remaining with pattern for verification
284        for (i, byte) in frame[PREFIX_SIZE..total].iter_mut().enumerate() {
285            *byte = ((PREFIX_SIZE + i) & 0xFF) as u8;
286        }
287        frame
288    }
289
290    /// Build an established direct-FSP frame. Its full cleartext header is
291    /// four bytes shorter than an established FMP frame.
292    fn build_direct_fsp_frame(payload_len: u16) -> Vec<u8> {
293        let remaining_header = crate::proto::fsp_wire::FSP_HEADER_SIZE - PREFIX_SIZE;
294        let total = PREFIX_SIZE + remaining_header + payload_len as usize + AEAD_TAG_SIZE;
295        let mut frame = vec![0u8; total];
296        frame[0] = 0x00;
297        frame[1] = crate::proto::fsp_wire::FSP_FLAG_DIRECT_TRANSPORT;
298        frame[2..4].copy_from_slice(&payload_len.to_le_bytes());
299        for (i, byte) in frame[PREFIX_SIZE..total].iter_mut().enumerate() {
300            *byte = (0x80 | ((PREFIX_SIZE + i) & 0x7F)) as u8;
301        }
302        frame
303    }
304
305    /// Build a msg1 frame (114 bytes total).
306    fn build_msg1_frame() -> Vec<u8> {
307        let mut frame = vec![0xAA; MSG1_WIRE_SIZE];
308        frame[0] = 0x01; // ver=0, phase=1
309        frame[1] = 0x00; // flags
310        frame[2..4].copy_from_slice(&MSG1_PAYLOAD_LEN.to_le_bytes());
311        frame
312    }
313
314    /// Build a msg2 frame (69 bytes total).
315    fn build_msg2_frame() -> Vec<u8> {
316        let mut frame = vec![0xBB; MSG2_WIRE_SIZE];
317        frame[0] = 0x02; // ver=0, phase=2
318        frame[1] = 0x00; // flags
319        frame[2..4].copy_from_slice(&MSG2_PAYLOAD_LEN.to_le_bytes());
320        frame
321    }
322
323    #[tokio::test]
324    async fn test_read_established_frame() {
325        let payload_len = 64u16;
326        let frame = build_established_frame(payload_len);
327        let expected = frame.clone();
328
329        let mut cursor = Cursor::new(frame);
330        let packet = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
331        assert_eq!(packet, expected);
332    }
333
334    #[tokio::test]
335    async fn test_read_msg1_frame() {
336        let frame = build_msg1_frame();
337        let expected = frame.clone();
338
339        let mut cursor = Cursor::new(frame);
340        let packet = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
341        assert_eq!(packet.len(), MSG1_WIRE_SIZE);
342        assert_eq!(packet, expected);
343    }
344
345    #[tokio::test]
346    async fn test_read_msg2_frame() {
347        let frame = build_msg2_frame();
348        let expected = frame.clone();
349
350        let mut cursor = Cursor::new(frame);
351        let packet = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
352        assert_eq!(packet.len(), MSG2_WIRE_SIZE);
353        assert_eq!(packet, expected);
354    }
355
356    #[tokio::test]
357    async fn test_read_multiple_packets() {
358        let mut data = Vec::new();
359        let msg1 = build_msg1_frame();
360        let est = build_established_frame(32);
361        let msg2 = build_msg2_frame();
362        data.extend_from_slice(&msg1);
363        data.extend_from_slice(&est);
364        data.extend_from_slice(&msg2);
365
366        let mut cursor = Cursor::new(data);
367        let p1 = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
368        assert_eq!(p1.len(), MSG1_WIRE_SIZE);
369
370        let p2 = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
371        assert_eq!(p2, est);
372
373        let p3 = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
374        assert_eq!(p3.len(), MSG2_WIRE_SIZE);
375    }
376
377    #[tokio::test]
378    async fn direct_fsp_and_fmp_frames_keep_exact_stream_boundaries() {
379        let direct_fsp = build_direct_fsp_frame(23);
380        let fmp = build_established_frame(31);
381        let mut data = direct_fsp.clone();
382        data.extend_from_slice(&fmp);
383
384        let mut cursor = Cursor::new(data);
385        assert_eq!(
386            read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap(),
387            direct_fsp
388        );
389        assert_eq!(
390            read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap(),
391            fmp
392        );
393    }
394
395    #[tokio::test]
396    async fn direct_fsp_accepts_full_wire_range_and_rejects_bad_shape_or_flags() {
397        let max_payload = build_direct_fsp_frame(u16::MAX);
398        assert_eq!(
399            read_fmp_packet(&mut Cursor::new(max_payload.clone()), READ_TIMEOUT)
400                .await
401                .unwrap(),
402            max_payload
403        );
404
405        let mut truncated = build_direct_fsp_frame(23);
406        truncated.pop();
407        assert!(matches!(
408            read_fmp_packet(&mut Cursor::new(truncated), READ_TIMEOUT).await,
409            Err(StreamError::Io(_))
410        ));
411
412        let mut extra_byte = build_direct_fsp_frame(23);
413        extra_byte.push(0);
414        assert!(matches!(
415            validate_stream_record(&extra_byte),
416            Err(StreamError::Io(error)) if error.kind() == std::io::ErrorKind::InvalidData
417        ));
418
419        let mut invalid_flags = build_direct_fsp_frame(23);
420        invalid_flags[1] |= crate::proto::fsp_wire::FSP_FLAG_U;
421        assert!(matches!(
422            read_fmp_packet(&mut Cursor::new(invalid_flags), READ_TIMEOUT).await,
423            Err(StreamError::Io(error)) if error.kind() == std::io::ErrorKind::InvalidData
424        ));
425
426        let mut invalid_flags = build_direct_fsp_frame(23);
427        invalid_flags[1] |= crate::proto::fsp_wire::FSP_FLAG_CP;
428        assert!(matches!(
429            read_fmp_packet(&mut Cursor::new(invalid_flags), READ_TIMEOUT).await,
430            Err(StreamError::Io(error)) if error.kind() == std::io::ErrorKind::InvalidData
431        ));
432
433        let mut rekey_epoch = build_direct_fsp_frame(23);
434        rekey_epoch[1] |= crate::proto::fsp_wire::FSP_FLAG_K;
435        assert_eq!(
436            read_fmp_packet(&mut Cursor::new(rekey_epoch.clone()), READ_TIMEOUT)
437                .await
438                .unwrap(),
439            rekey_epoch,
440            "DIRECT and the encrypted epoch K bit are compatible"
441        );
442    }
443
444    #[tokio::test]
445    async fn test_unknown_version_error() {
446        // TLS ClientHello starts with 0x16 (record type "Handshake"),
447        // which parses as FMP version=1, phase=6.
448        let mut frame = vec![0u8; 100];
449        frame[0] = 0x16;
450        let mut cursor = Cursor::new(frame);
451        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
452            .await
453            .unwrap_err();
454        assert!(matches!(err, StreamError::UnknownVersion(1)));
455    }
456
457    #[tokio::test]
458    async fn test_unknown_phase_error() {
459        let mut frame = vec![0u8; 100];
460        frame[0] = 0x05; // unknown phase
461        frame[2..4].copy_from_slice(&10u16.to_le_bytes());
462
463        let mut cursor = Cursor::new(frame);
464        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
465            .await
466            .unwrap_err();
467        assert!(matches!(err, StreamError::UnknownPhase(0x5)));
468    }
469
470    #[tokio::test]
471    async fn test_handshake_size_mismatch_msg1() {
472        let mut frame = vec![0u8; 200];
473        frame[0] = 0x01; // msg1
474        // Wrong payload_len (should be 110)
475        frame[2..4].copy_from_slice(&50u16.to_le_bytes());
476
477        let mut cursor = Cursor::new(frame);
478        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
479            .await
480            .unwrap_err();
481        assert!(matches!(
482            err,
483            StreamError::HandshakeSizeMismatch { phase: 0x1, .. }
484        ));
485    }
486
487    #[tokio::test]
488    async fn test_handshake_size_mismatch_msg2() {
489        let mut frame = vec![0u8; 200];
490        frame[0] = 0x02; // msg2
491        // Wrong payload_len (should be 65)
492        frame[2..4].copy_from_slice(&50u16.to_le_bytes());
493
494        let mut cursor = Cursor::new(frame);
495        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
496            .await
497            .unwrap_err();
498        assert!(matches!(
499            err,
500            StreamError::HandshakeSizeMismatch { phase: 0x2, .. }
501        ));
502    }
503
504    #[tokio::test]
505    async fn test_eof_on_prefix() {
506        // Only 2 bytes available (need 4 for prefix)
507        let data = vec![0u8; 2];
508        let mut cursor = Cursor::new(data);
509        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
510            .await
511            .unwrap_err();
512        assert!(matches!(err, StreamError::Io(_)));
513    }
514
515    #[tokio::test]
516    async fn test_eof_on_body() {
517        // Valid msg1 prefix but truncated body
518        let mut data = vec![0u8; 10]; // need 114 total
519        data[0] = 0x01; // msg1
520        data[2..4].copy_from_slice(&MSG1_PAYLOAD_LEN.to_le_bytes());
521
522        let mut cursor = Cursor::new(data);
523        let err = read_fmp_packet(&mut cursor, READ_TIMEOUT)
524            .await
525            .unwrap_err();
526        assert!(matches!(err, StreamError::Io(_)));
527    }
528
529    #[tokio::test]
530    async fn test_zero_payload_established() {
531        // payload_len = 0 is valid (header-only encrypted frame with tag)
532        let frame = build_established_frame(0);
533        let expected_len = PREFIX_SIZE + FMP_ESTABLISHED_REMAINING_HEADER + AEAD_TAG_SIZE;
534        assert_eq!(frame.len(), expected_len);
535
536        let mut cursor = Cursor::new(frame.clone());
537        let packet = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
538        assert_eq!(packet.len(), expected_len);
539        assert_eq!(packet, frame);
540    }
541
542    #[tokio::test]
543    async fn test_max_fmp_payload_uses_the_full_wire_length() {
544        let frame = build_established_frame(u16::MAX);
545
546        let mut cursor = Cursor::new(frame.clone());
547        let packet = read_fmp_packet(&mut cursor, READ_TIMEOUT).await.unwrap();
548        assert_eq!(packet, frame);
549        validate_stream_record(&frame).unwrap();
550    }
551
552    #[tokio::test]
553    async fn public_reader_keeps_the_released_mtu_api_and_error_variant() {
554        let frame = build_established_frame(64);
555        assert_eq!(
556            super::read_fmp_packet(&mut Cursor::new(frame.clone()), 1400)
557                .await
558                .unwrap(),
559            frame
560        );
561
562        let over_budget = build_established_frame(1369);
563        assert!(matches!(
564            super::read_fmp_packet(&mut Cursor::new(over_budget), 1400).await,
565            Err(StreamError::PayloadTooLarge {
566                payload_len: 1369,
567                max_payload_len: 1368,
568            })
569        ));
570    }
571
572    #[tokio::test]
573    async fn idle_wait_is_unbounded_but_partial_prefix_and_body_are_bounded() {
574        let completion = Duration::from_millis(30);
575
576        let (mut writer, mut reader) = tokio::io::duplex(256);
577        let idle_read = tokio::spawn(async move { read_fmp_packet(&mut reader, completion).await });
578        tokio::time::sleep(completion + Duration::from_millis(10)).await;
579        let frame = build_direct_fsp_frame(7);
580        tokio::io::AsyncWriteExt::write_all(&mut writer, &frame)
581            .await
582            .unwrap();
583        assert_eq!(idle_read.await.unwrap().unwrap(), frame);
584
585        let (mut writer, mut reader) = tokio::io::duplex(256);
586        tokio::io::AsyncWriteExt::write_all(&mut writer, &[0x00])
587            .await
588            .unwrap();
589        assert!(matches!(
590            read_fmp_packet(&mut reader, completion).await,
591            Err(StreamError::Io(error)) if error.kind() == std::io::ErrorKind::TimedOut
592        ));
593
594        let (mut writer, mut reader) = tokio::io::duplex(256);
595        tokio::io::AsyncWriteExt::write_all(&mut writer, &[0x00, 0x00, 0x04, 0x00, 0xAA])
596            .await
597            .unwrap();
598        assert!(matches!(
599            read_fmp_packet(&mut reader, completion).await,
600            Err(StreamError::Io(error)) if error.kind() == std::io::ErrorKind::TimedOut
601        ));
602    }
603}