1#![expect(missing_docs)]
5#![forbid(unsafe_code)]
6
7pub mod protocol;
8
9use futures::StreamExt;
10use guid::Guid;
11use inspect::Inspect;
12use protocol::HEADER_SIZE;
13use protocol::MAX_MESSAGE_SIZE;
14use protocol::MessageHeader;
15use protocol::VmbusMessage;
16use std::str::FromStr;
17use std::task::Poll;
18use thiserror::Error;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21use zerocopy::KnownLayout;
22
23pub const VMBUS_SINT: u8 = 2;
25
26#[derive(Debug)]
27pub struct TaggedStream<T, S>(Option<T>, S);
28
29impl<T: Clone, S: futures::Stream + Unpin> TaggedStream<T, S> {
30 pub fn new(t: T, s: S) -> Self {
31 Self(Some(t), s)
32 }
33
34 pub fn value(&self) -> Option<&T> {
35 self.0.as_ref()
36 }
37}
38
39impl<T: Clone, S: futures::Stream + Unpin> futures::Stream for TaggedStream<T, S>
40where
41 Self: Unpin,
42{
43 type Item = (T, Option<S::Item>);
44
45 fn poll_next(
46 self: std::pin::Pin<&mut Self>,
47 cx: &mut std::task::Context<'_>,
48 ) -> Poll<Option<Self::Item>> {
49 let this = self.get_mut();
50 if let Some(t) = this.0.clone() {
51 let v = std::task::ready!(this.1.poll_next_unpin(cx));
52 if v.is_none() {
53 this.0 = None;
55 }
56 Poll::Ready(Some((t, v)))
57 } else {
58 Poll::Ready(None)
59 }
60 }
61}
62
63#[derive(Copy, Clone, Debug, PartialEq, Eq, Inspect)]
65pub struct VersionInfo {
66 pub version: protocol::Version,
67 pub feature_flags: protocol::FeatureFlags,
68}
69
70#[derive(Copy, Clone, Debug)]
72pub struct MaxVersionInfo {
73 pub version: u32,
74 pub feature_flags: protocol::FeatureFlags,
75}
76
77impl MaxVersionInfo {
78 pub fn new(version: u32) -> Self {
79 Self {
80 version,
81 feature_flags: protocol::FeatureFlags::new(),
82 }
83 }
84}
85
86impl From<VersionInfo> for MaxVersionInfo {
87 fn from(info: VersionInfo) -> Self {
88 Self {
89 version: info.version as u32,
90 feature_flags: info.feature_flags,
91 }
92 }
93}
94
95pub fn parse_vmbus_version(value: &str) -> Result<u32, String> {
99 || -> Option<u32> {
100 let (major, minor) = value.split_once('.')?;
101 let major = u16::from_str(major).ok()?;
102 let minor = u16::from_str(minor).ok()?;
103 Some(protocol::make_version(major, minor))
104 }()
105 .ok_or_else(|| format!("invalid vmbus version '{}'", value))
106}
107
108#[derive(Clone, Debug)]
109pub struct OutgoingMessage {
110 data: [u8; MAX_MESSAGE_SIZE],
111 len: u8,
112}
113
114impl OutgoingMessage {
116 pub fn new<T: IntoBytes + Immutable + KnownLayout + VmbusMessage>(message: &T) -> Self {
118 let mut data = [0; MAX_MESSAGE_SIZE];
119 let header = MessageHeader::new(T::MESSAGE_TYPE);
120 let message_bytes = message.as_bytes();
121 let len = HEADER_SIZE + message_bytes.len();
122 data[..HEADER_SIZE].copy_from_slice(header.as_bytes());
123 data[HEADER_SIZE..len].copy_from_slice(message_bytes);
124 Self {
125 data,
126 len: len as u8,
127 }
128 }
129
130 pub fn with_data<T: IntoBytes + Immutable + KnownLayout + VmbusMessage>(
133 message: &T,
134 data: &[u8],
135 ) -> Self {
136 let mut message = OutgoingMessage::new(message);
137 let old_len = message.len as usize;
138 let len = old_len + data.len();
139 message.data[old_len..len].copy_from_slice(data);
140 message.len = len as u8;
141 message
142 }
143
144 pub fn from_message(message: &[u8]) -> Result<Self, MessageTooLarge> {
147 if message.len() > MAX_MESSAGE_SIZE {
148 return Err(MessageTooLarge);
149 }
150 let mut data = [0; MAX_MESSAGE_SIZE];
151 data[0..message.len()].copy_from_slice(message);
152 Ok(Self {
153 data,
154 len: message.len() as u8,
155 })
156 }
157
158 pub fn data(&self) -> &[u8] {
160 &self.data[..self.len as usize]
161 }
162}
163
164impl PartialEq for OutgoingMessage {
165 fn eq(&self, other: &Self) -> bool {
166 self.len == other.len && self.data[..self.len as usize] == other.data[..self.len as usize]
167 }
168}
169
170#[derive(Debug, Error)]
171#[error("a synic message exceeds the maximum length")]
172pub struct MessageTooLarge;
173
174#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Inspect)]
176pub struct HvsockConnectRequest {
177 pub service_id: Guid,
178 pub endpoint_id: Guid,
179 pub silo_id: Guid,
180 pub hosted_silo_unaware: bool,
181}
182
183impl HvsockConnectRequest {
184 pub fn from_message(value: protocol::TlConnectRequest2, hosted_silo_unaware: bool) -> Self {
185 Self {
186 service_id: value.base.service_id,
187 endpoint_id: value.base.endpoint_id,
188 silo_id: value.silo_id,
189 hosted_silo_unaware,
190 }
191 }
192}
193
194impl From<HvsockConnectRequest> for protocol::TlConnectRequest2 {
195 fn from(value: HvsockConnectRequest) -> Self {
196 Self {
197 base: protocol::TlConnectRequest {
198 endpoint_id: value.endpoint_id,
199 service_id: value.service_id,
200 },
201 silo_id: value.silo_id,
202 }
203 }
204}
205
206#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
208pub struct HvsockConnectResult {
209 pub service_id: Guid,
210 pub endpoint_id: Guid,
211 pub success: bool,
212}
213
214impl HvsockConnectResult {
215 pub fn from_request(request: &HvsockConnectRequest, success: bool) -> Self {
217 Self {
218 service_id: request.service_id,
219 endpoint_id: request.endpoint_id,
220 success,
221 }
222 }
223}
224
225impl From<protocol::TlConnectResult> for HvsockConnectResult {
226 fn from(value: protocol::TlConnectResult) -> Self {
227 Self {
228 service_id: value.service_id,
229 endpoint_id: value.endpoint_id,
230 success: value.status == protocol::STATUS_SUCCESS,
231 }
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use crate::protocol::ChannelId;
239 use crate::protocol::GpadlId;
240
241 #[test]
242 fn test_outgoing_message() {
243 let message = OutgoingMessage::new(&protocol::CloseChannel {
244 channel_id: ChannelId(5),
245 });
246
247 assert_eq!(&[0x7, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0], message.data())
248 }
249
250 #[test]
251 fn test_outgoing_message_empty() {
252 let message = OutgoingMessage::new(&protocol::Unload {});
253
254 assert_eq!(&[0x10, 0, 0, 0, 0, 0, 0, 0], message.data())
255 }
256
257 #[test]
258 fn test_outgoing_message_with_data() {
259 let message = OutgoingMessage::with_data(
260 &protocol::GpadlHeader {
261 channel_id: ChannelId(5),
262 gpadl_id: GpadlId(1),
263 len: 7,
264 count: 6,
265 },
266 &[0xa, 0xb, 0xc, 0xd],
267 );
268
269 assert_eq!(
270 &[
271 0x8, 0, 0, 0, 0, 0, 0, 0, 0x5, 0, 0, 0, 0x1, 0, 0, 0, 0x7, 0, 0x6, 0, 0xa, 0xb,
272 0xc, 0xd
273 ],
274 message.data()
275 )
276 }
277}