1use packet::{
14 BufferView, FragmentedBytesMut, PacketBuilder, PacketConstraints, ParsablePacket,
15 ParseMetadata, SerializeTarget,
16};
17use std::num::NonZeroU16;
18use zerocopy::byteorder::little_endian::U32;
19use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
20
21pub use witness::ErrorValue;
23
24pub const SERVER_PORT: NonZeroU16 = NonZeroU16::new(33330).unwrap();
26pub const ADVERT_PORT: NonZeroU16 = NonZeroU16::new(33331).unwrap();
28
29const MAGIC: u32 = 0xAA774217;
30
31mod witness {
32 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
37 pub struct ErrorValue(u32);
38
39 impl ErrorValue {
40 const ERROR_MASK: u32 = 0x80000000;
41
42 pub const fn new(v: u32) -> Option<Self> {
45 if v & Self::ERROR_MASK != 0 {
46 Some(Self(v))
47 } else {
48 None
49 }
50 }
51 }
52
53 impl From<ErrorValue> for u32 {
54 fn from(v: ErrorValue) -> Self {
55 let ErrorValue(v) = v;
56 v
57 }
58 }
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
65pub enum Opcode {
66 Command,
67 SendFile,
68 Data,
69 Boot,
70 Query,
71 ShellCmd,
72 Open,
73 Read,
74 Write,
75 Close,
76 LastData,
77 Reboot,
78 GetAdvert,
79 Ack,
80 FileReceived,
81 Advertise,
82}
83
84impl From<Opcode> for u32 {
85 fn from(value: Opcode) -> u32 {
86 match value {
87 Opcode::Command => 1,
88 Opcode::SendFile => 2,
89 Opcode::Data => 3,
90 Opcode::Boot => 4,
91 Opcode::Query => 5,
92 Opcode::ShellCmd => 6,
93 Opcode::Open => 7,
94 Opcode::Read => 8,
95 Opcode::Write => 9,
96 Opcode::Close => 10,
97 Opcode::LastData => 11,
98 Opcode::Reboot => 12,
99 Opcode::GetAdvert => 13,
100 Opcode::Ack => 0,
101 Opcode::FileReceived => 0x70000001,
102 Opcode::Advertise => 0x77777777,
103 }
104 }
105}
106
107#[derive(Debug, Copy, Clone, Eq, PartialEq)]
109pub enum ErrorCode {
110 BadCommand,
111 BadParam,
112 TooLarge,
113 BadFile,
114 Unknown(ErrorValue),
115}
116
117impl From<ErrorCode> for u32 {
118 fn from(value: ErrorCode) -> u32 {
119 match value {
120 ErrorCode::BadCommand => 0x80000001,
121 ErrorCode::BadParam => 0x80000002,
122 ErrorCode::TooLarge => 0x80000003,
123 ErrorCode::BadFile => 0x80000004,
124 ErrorCode::Unknown(v) => v.into(),
125 }
126 }
127}
128
129#[derive(Debug, Copy, Clone, Eq, PartialEq)]
133pub enum OpcodeOrErr {
134 Op(Opcode),
135 Err(ErrorCode),
136}
137
138impl From<Opcode> for OpcodeOrErr {
139 fn from(op: Opcode) -> Self {
140 OpcodeOrErr::Op(op)
141 }
142}
143
144impl From<ErrorCode> for OpcodeOrErr {
145 fn from(err: ErrorCode) -> Self {
146 OpcodeOrErr::Err(err)
147 }
148}
149
150impl TryFrom<u32> for OpcodeOrErr {
151 type Error = u32;
152
153 fn try_from(value: u32) -> Result<Self, Self::Error> {
154 match value {
155 1 => Ok(Opcode::Command.into()),
156 2 => Ok(Opcode::SendFile.into()),
157 3 => Ok(Opcode::Data.into()),
158 4 => Ok(Opcode::Boot.into()),
159 5 => Ok(Opcode::Query.into()),
160 6 => Ok(Opcode::ShellCmd.into()),
161 7 => Ok(Opcode::Open.into()),
162 8 => Ok(Opcode::Read.into()),
163 9 => Ok(Opcode::Write.into()),
164 10 => Ok(Opcode::Close.into()),
165 11 => Ok(Opcode::LastData.into()),
166 12 => Ok(Opcode::Reboot.into()),
167 13 => Ok(Opcode::GetAdvert.into()),
168 0 => Ok(Opcode::Ack.into()),
169 0x70000001 => Ok(Opcode::FileReceived.into()),
170 0x77777777 => Ok(Opcode::Advertise.into()),
171 0x80000001 => Ok(ErrorCode::BadCommand.into()),
172 0x80000002 => Ok(ErrorCode::BadParam.into()),
173 0x80000003 => Ok(ErrorCode::TooLarge.into()),
174 0x80000004 => Ok(ErrorCode::BadFile.into()),
175 v => match ErrorValue::new(v) {
176 Some(e) => Ok(ErrorCode::Unknown(e).into()),
177 None => Err(v),
178 },
179 }
180 }
181}
182
183impl From<OpcodeOrErr> for u32 {
184 fn from(cmd: OpcodeOrErr) -> Self {
185 match cmd {
186 OpcodeOrErr::Op(op) => op.into(),
187 OpcodeOrErr::Err(e) => e.into(),
188 }
189 }
190}
191
192#[derive(Debug)]
194pub enum ParseError {
195 Malformed,
196 UnknownOpcode(u32),
197 BadMagic,
198}
199
200#[repr(C)]
201#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Debug)]
202struct MessageHead {
203 magic: U32,
204 cookie: U32,
205 cmd: U32,
206 arg: U32,
207}
208
209#[derive(Debug)]
211pub struct NetbootPacket<B: SplitByteSlice> {
212 command: OpcodeOrErr,
213 message: Ref<B, MessageHead>,
214 payload: B,
215}
216
217impl<B: SplitByteSlice> NetbootPacket<B> {
218 pub fn command(&self) -> OpcodeOrErr {
219 self.command
220 }
221
222 pub fn cookie(&self) -> u32 {
223 self.message.cookie.get()
224 }
225
226 pub fn arg(&self) -> u32 {
227 self.message.arg.get()
228 }
229
230 pub fn payload(&self) -> &[u8] {
231 self.payload.as_ref()
232 }
233}
234
235impl<B: SplitByteSlice> ParsablePacket<B, ()> for NetbootPacket<B> {
236 type Error = ParseError;
237
238 fn parse<BV: BufferView<B>>(mut buffer: BV, _args: ()) -> Result<Self, Self::Error> {
239 let message = buffer.take_obj_front::<MessageHead>().ok_or(ParseError::Malformed)?;
240 if message.magic.get() != MAGIC {
241 return Err(ParseError::BadMagic);
242 }
243 let opcode = message.cmd.get().try_into().map_err(ParseError::UnknownOpcode)?;
244 let payload = buffer.into_rest();
245 Ok(Self { command: opcode, message, payload })
246 }
247
248 fn parse_metadata(&self) -> ParseMetadata {
249 unimplemented!()
252 }
253}
254
255#[derive(Debug)]
257pub struct NetbootPacketBuilder {
258 cmd: OpcodeOrErr,
259 cookie: u32,
260 arg: u32,
261}
262
263impl NetbootPacketBuilder {
264 pub fn new(cmd: OpcodeOrErr, cookie: u32, arg: u32) -> Self {
265 Self { cmd, cookie, arg }
266 }
267}
268
269impl PacketBuilder for NetbootPacketBuilder {
270 fn constraints(&self) -> PacketConstraints {
271 PacketConstraints::new(std::mem::size_of::<MessageHead>(), 0, 0, std::usize::MAX)
272 }
273
274 fn serialize(&self, target: &mut SerializeTarget<'_>, _body: FragmentedBytesMut<'_, '_>) {
275 let mut bv = crate::as_buffer_view_mut(&mut target.header);
276 let mut message = bv.take_obj_front::<MessageHead>().expect("not enough space in buffer");
277 let MessageHead { magic, cookie, cmd, arg } = &mut *message;
278 magic.set(MAGIC);
279 cookie.set(self.cookie);
280 arg.set(self.arg);
281 cmd.set(self.cmd.into());
282 }
283}
284
285#[cfg(test)]
286mod tests {
287
288 use super::*;
289
290 use assert_matches::assert_matches;
291 use packet::{InnerPacketBuilder as _, ParseBuffer as _, Serializer as _};
292
293 #[test]
294 fn test_parse_serialize() {
295 const PAYLOAD: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
296 let mut pkt = (&PAYLOAD[..])
297 .into_serializer()
298 .encapsulate(NetbootPacketBuilder::new(Opcode::Ack.into(), 3, 4))
299 .serialize_vec_outer()
300 .expect("failed to serialize");
301 let parsed = pkt.parse::<NetbootPacket<_>>().expect("failed to parse");
302 assert_eq!(parsed.command(), OpcodeOrErr::Op(Opcode::Ack));
303 assert_eq!(parsed.cookie(), 3);
304 assert_eq!(parsed.arg(), 4);
305 assert_eq!(parsed.payload(), &PAYLOAD[..]);
306 }
307
308 #[test]
309 fn test_parse_serialize_opcodes() {
310 const TEST_OPCODES: [Opcode; 16] = [
311 Opcode::Command,
312 Opcode::SendFile,
313 Opcode::Data,
314 Opcode::Boot,
315 Opcode::Query,
316 Opcode::ShellCmd,
317 Opcode::Open,
318 Opcode::Read,
319 Opcode::Write,
320 Opcode::Close,
321 Opcode::LastData,
322 Opcode::Reboot,
323 Opcode::GetAdvert,
324 Opcode::Ack,
325 Opcode::FileReceived,
326 Opcode::Advertise,
327 ];
328
329 for opcode in TEST_OPCODES.iter() {
330 match opcode {
331 Opcode::Command
332 | Opcode::SendFile
333 | Opcode::Data
334 | Opcode::Boot
335 | Opcode::Query
336 | Opcode::ShellCmd
337 | Opcode::Open
338 | Opcode::Read
339 | Opcode::Write
340 | Opcode::Close
341 | Opcode::LastData
342 | Opcode::Reboot
343 | Opcode::GetAdvert
344 | Opcode::Ack
345 | Opcode::FileReceived
346 | Opcode::Advertise => {
347 }
350 }
351 let opcode_or_err = OpcodeOrErr::try_from(u32::from(*opcode)).expect("failed to parse");
352 assert_matches!(opcode_or_err, OpcodeOrErr::Op(op) if op == *opcode);
353 }
354 }
355
356 #[test]
357 fn test_parse_serialize_error_codes() {
358 let test_error_codes = [
359 ErrorCode::BadCommand,
360 ErrorCode::BadParam,
361 ErrorCode::TooLarge,
362 ErrorCode::BadFile,
363 ErrorCode::Unknown(ErrorValue::new(0x80001234).unwrap()),
364 ];
365 for error in test_error_codes.iter() {
366 match error {
367 ErrorCode::BadCommand
368 | ErrorCode::BadParam
369 | ErrorCode::TooLarge
370 | ErrorCode::BadFile
371 | ErrorCode::Unknown(_) => {
372 }
375 }
376 let opcode_or_err = OpcodeOrErr::try_from(u32::from(*error)).expect("failed to parse");
377 assert_matches!(opcode_or_err, OpcodeOrErr::Err(e) if e == *error);
378 }
379 }
380}