Skip to main content

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