Skip to main content

steamkit/base/
packet_base.rs

1//! Messages as received from the network: the parsed header plus the offset
2//! where the body starts. The body itself is parsed later, by the handler that
3//! wants it.
4
5use crate::base::generated::steam_language::EMsg;
6use crate::base::generated::steam_language_internal::{
7    ExtendedClientMsgHdr, MsgHdr, MsgHdrProtoBuf, SteamSerializableDeserialize,
8};
9use crate::base::msg_base::Reader;
10use crate::error::SteamError;
11use crate::types::job_id::JobID;
12
13/// A message received from the network.
14///
15/// Unlike [`IClientMsg`](crate::base::client_msg::IClientMsg), this only gives
16/// access to the header and the raw bytes.
17pub trait IPacketMsg: Send + Sync {
18    /// Whether this packet message is protobuf backed.
19    fn is_proto(&self) -> bool;
20
21    /// The network message type of this packet message.
22    fn msg_type(&self) -> EMsg;
23
24    /// The target job id of this packet message.
25    fn target_job_id(&self) -> JobID;
26
27    /// The source job id of this packet message.
28    fn source_job_id(&self) -> JobID;
29
30    /// The raw bytes of the message.
31    fn get_data(&self) -> &[u8];
32}
33
34/// Returns the message type of `msg`, or an `InvalidArgument` error naming
35/// `name` if there is no message.
36pub fn get_msg_type_with_null_check(
37    msg: Option<&dyn IPacketMsg>,
38    name: &str,
39) -> Result<EMsg, SteamError> {
40    match msg {
41        Some(msg) => Ok(msg.msg_type()),
42        None => Err(SteamError::InvalidArgument(format!(
43            "{name} must not be null"
44        ))),
45    }
46}
47
48/// A protobuf-backed packet.
49pub struct PacketClientMsgProtobuf {
50    msg_type: EMsg,
51    /// The parsed header of this packet.
52    pub(crate) header: MsgHdrProtoBuf,
53    /// The offset of the body inside the raw data.
54    pub(crate) body_offset: u64,
55    payload: Vec<u8>,
56}
57
58impl PacketClientMsgProtobuf {
59    /// Parses the protobuf header of `data` so the job ids can be read. The
60    /// body starts right after the header.
61    pub fn new(e_msg: EMsg, data: Vec<u8>) -> Result<Self, SteamError> {
62        let mut reader = Reader::new(&data);
63        let header = MsgHdrProtoBuf::read(&mut reader)?;
64        Ok(PacketClientMsgProtobuf {
65            msg_type: e_msg,
66            header,
67            body_offset: reader.position() as u64,
68            payload: data,
69        })
70    }
71
72    /// The header of this packet message.
73    pub fn header(&self) -> &MsgHdrProtoBuf {
74        &self.header
75    }
76
77    /// The offset of the body inside the raw data.
78    pub fn body_offset(&self) -> u64 {
79        self.body_offset
80    }
81}
82
83impl IPacketMsg for PacketClientMsgProtobuf {
84    fn is_proto(&self) -> bool {
85        true
86    }
87
88    fn msg_type(&self) -> EMsg {
89        self.msg_type
90    }
91
92    fn target_job_id(&self) -> JobID {
93        JobID::from_value(self.header.proto.jobid_target.unwrap_or(u64::MAX))
94    }
95
96    fn source_job_id(&self) -> JobID {
97        JobID::from_value(self.header.proto.jobid_source.unwrap_or(u64::MAX))
98    }
99
100    fn get_data(&self) -> &[u8] {
101        &self.payload
102    }
103}
104
105/// A packet with an extended header.
106pub struct PacketClientMsg {
107    msg_type: EMsg,
108    /// The parsed header of this packet.
109    pub(crate) header: ExtendedClientMsgHdr,
110    /// The offset of the body inside the raw data.
111    pub(crate) body_offset: u64,
112    payload: Vec<u8>,
113}
114
115impl PacketClientMsg {
116    /// Parses the extended header of `data` so the job ids can be read. The
117    /// body starts right after the header.
118    pub fn new(e_msg: EMsg, data: Vec<u8>) -> Result<Self, SteamError> {
119        let mut reader = Reader::new(&data);
120        let header = ExtendedClientMsgHdr::read(&mut reader)?;
121        Ok(PacketClientMsg {
122            msg_type: e_msg,
123            header,
124            body_offset: reader.position() as u64,
125            payload: data,
126        })
127    }
128
129    /// The header of this packet message.
130    pub fn header(&self) -> &ExtendedClientMsgHdr {
131        &self.header
132    }
133
134    /// The offset of the body inside the raw data.
135    pub fn body_offset(&self) -> u64 {
136        self.body_offset
137    }
138}
139
140impl IPacketMsg for PacketClientMsg {
141    fn is_proto(&self) -> bool {
142        false
143    }
144
145    fn msg_type(&self) -> EMsg {
146        self.msg_type
147    }
148
149    fn target_job_id(&self) -> JobID {
150        JobID::from_value(self.header.target_job_id)
151    }
152
153    fn source_job_id(&self) -> JobID {
154        JobID::from_value(self.header.source_job_id)
155    }
156
157    fn get_data(&self) -> &[u8] {
158        &self.payload
159    }
160}
161
162/// A packet with a basic header.
163pub struct PacketMsg {
164    msg_type: EMsg,
165    /// The parsed header of this packet.
166    pub(crate) header: MsgHdr,
167    /// The offset of the body inside the raw data.
168    pub(crate) body_offset: u64,
169    payload: Vec<u8>,
170}
171
172impl PacketMsg {
173    /// Parses the header of `data` so the job ids can be read. The body starts
174    /// right after the header.
175    pub fn new(e_msg: EMsg, data: Vec<u8>) -> Result<Self, SteamError> {
176        let mut reader = Reader::new(&data);
177        let header = MsgHdr::read(&mut reader)?;
178        Ok(PacketMsg {
179            msg_type: e_msg,
180            header,
181            body_offset: reader.position() as u64,
182            payload: data,
183        })
184    }
185
186    /// The header of this packet message.
187    pub fn header(&self) -> &MsgHdr {
188        &self.header
189    }
190
191    /// The offset of the body inside the raw data.
192    pub fn body_offset(&self) -> u64 {
193        self.body_offset
194    }
195}
196
197impl IPacketMsg for PacketMsg {
198    fn is_proto(&self) -> bool {
199        false
200    }
201
202    fn msg_type(&self) -> EMsg {
203        self.msg_type
204    }
205
206    fn target_job_id(&self) -> JobID {
207        JobID::from_value(self.header.target_job_id)
208    }
209
210    fn source_job_id(&self) -> JobID {
211        JobID::from_value(self.header.source_job_id)
212    }
213
214    fn get_data(&self) -> &[u8] {
215        &self.payload
216    }
217}