parsec_interface/requests/common/
wire_header_1_0.rs

1// Copyright 2020 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module defines and implements the raw wire protocol header frame for
5//! version 1.0 of the protocol.
6use crate::requests::common::MAGIC_NUMBER;
7use crate::requests::{ResponseStatus, Result};
8#[cfg(feature = "fuzz")]
9use arbitrary::Arbitrary;
10use bincode::Options;
11use log::error;
12use serde::{Deserialize, Serialize};
13use std::convert::TryFrom;
14use std::io::{Read, Write};
15
16const WIRE_PROTOCOL_VERSION_MAJ: u8 = 1;
17const WIRE_PROTOCOL_VERSION_MIN: u8 = 0;
18
19const REQUEST_HDR_SIZE: u16 = 30;
20
21/// Raw representation of a common request/response header, as defined for the wire format.
22///
23/// Serialisation and deserialisation are handled by `serde`, also in tune with the
24/// wire format (i.e. little-endian, native encoding).
25#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
26#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
27pub struct WireHeader {
28    /// Implementation-defined flags. Not used in Parsec currently. Must be present, but must be zero.
29    pub flags: u16,
30    /// Provider ID value
31    pub provider: u8,
32    /// Session handle
33    pub session: u64,
34    /// Content type: defines how the request body should be processed.
35    pub content_type: u8,
36    /// Accept type: defines how the service should provide its response.
37    pub accept_type: u8,
38    /// Authentication type.
39    pub auth_type: u8,
40    /// Number of bytes of content.
41    pub body_len: u32,
42    /// Number of bytes of authentication.
43    pub auth_len: u16,
44    /// Opcode of the operation to perform.
45    pub opcode: u32,
46    /// Response status of the request.
47    pub status: u16,
48    /// Reserved byte. Currently unused. Must be present. Must be zero.
49    pub reserved1: u8,
50    /// Reserved byte. Currently unused. Must be present. Must be zero.
51    pub reserved2: u8,
52}
53
54impl WireHeader {
55    /// Create a new raw wire header.
56    ///
57    /// For use in testing only.
58    #[cfg(feature = "testing")]
59    #[allow(clippy::new_without_default)]
60    pub fn new() -> WireHeader {
61        WireHeader {
62            flags: 0,
63            provider: 0,
64            session: 0,
65            content_type: 0,
66            accept_type: 0,
67            auth_type: 0,
68            body_len: 0,
69            auth_len: 0,
70            opcode: 0,
71            status: 0,
72            reserved1: 0,
73            reserved2: 0,
74        }
75    }
76
77    /// Serialise the request header and write the corresponding bytes to the given
78    /// stream.
79    ///
80    /// # Errors
81    /// - if marshalling the header fails, `ResponseStatus::InvalidEncoding` is returned.
82    /// - if writing the header bytes fails, `ResponseStatus::ConnectionError` is returned.
83    pub fn write_to_stream<W: Write>(&self, stream: &mut W) -> Result<()> {
84        let serializer = bincode::DefaultOptions::new()
85            .with_little_endian()
86            .with_fixint_encoding();
87
88        stream.write_all(&serializer.serialize(&MAGIC_NUMBER)?)?;
89
90        stream.write_all(&serializer.serialize(&REQUEST_HDR_SIZE)?)?;
91
92        stream.write_all(&serializer.serialize(&WIRE_PROTOCOL_VERSION_MAJ)?)?;
93        stream.write_all(&serializer.serialize(&WIRE_PROTOCOL_VERSION_MIN)?)?;
94
95        stream.write_all(&serializer.serialize(&self)?)?;
96
97        Ok(())
98    }
99
100    /// Deserialise a request header from the given stream.
101    ///
102    /// # Errors
103    /// - if either the magic number, the header size or the reserved fields
104    /// are invalid values, `ResponseStatus::InvalidHeader` is returned.
105    /// - if reading the fields after magic number and header size fails,
106    /// `ResponseStatus::ConnectionError` is returned
107    ///     - the read may fail due to a timeout if not enough bytes are
108    ///     sent across
109    /// - if the parsed bytes cannot be unmarshalled into the contained fields,
110    /// `ResponseStatus::InvalidEncoding` is returned.
111    /// - if the wire protocol version used is different than 1.0
112    pub fn read_from_stream<R: Read>(mut stream: &mut R) -> Result<WireHeader> {
113        let magic_number = get_from_stream!(stream, u32);
114        if magic_number != MAGIC_NUMBER {
115            error!(
116                "Expected magic number {}, got {}",
117                MAGIC_NUMBER, magic_number
118            );
119            return Err(ResponseStatus::InvalidHeader);
120        }
121
122        let hdr_size = get_from_stream!(stream, u16);
123        let mut bytes = vec![0_u8; usize::try_from(hdr_size)?];
124        stream.read_exact(&mut bytes)?;
125        if hdr_size != REQUEST_HDR_SIZE {
126            error!(
127                "Expected request header size {}, got {}",
128                REQUEST_HDR_SIZE, hdr_size
129            );
130            return Err(ResponseStatus::InvalidHeader);
131        }
132
133        let version_maj = bytes.remove(0); // first byte after hdr length is version maj
134        let version_min = bytes.remove(0); // second byte after hdr length is version min
135        if version_maj != WIRE_PROTOCOL_VERSION_MAJ || version_min != WIRE_PROTOCOL_VERSION_MIN {
136            error!(
137                "Expected wire protocol version {}.{}, got {}.{} instead",
138                WIRE_PROTOCOL_VERSION_MAJ, WIRE_PROTOCOL_VERSION_MIN, version_maj, version_min
139            );
140            return Err(ResponseStatus::WireProtocolVersionNotSupported);
141        }
142
143        let deserializer = bincode::DefaultOptions::new()
144            .with_little_endian()
145            .with_fixint_encoding();
146        let wire_header: WireHeader = deserializer.deserialize(&bytes)?;
147
148        if wire_header.reserved1 != 0x00 || wire_header.reserved2 != 0x00 {
149            Err(ResponseStatus::InvalidHeader)
150        } else {
151            Ok(wire_header)
152        }
153    }
154}