1use std::{any::type_name, fmt, marker::PhantomData, ops::Deref};
9
10use anyhow::{Context, Result, anyhow, bail};
11use bytes::{Bytes, BytesMut};
12use crc32c::crc32c_append;
13use zerocopy::{
14 BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32,
15};
16
17use crate::{
18 cfg::{
19 config::Config,
20 enums::{Digest, YesNo},
21 },
22 client::pdu_connection::FromBytes,
23 models::{
24 common::{BasicHeaderSegment, Builder, HEADER_LEN, SendingData},
25 data::sense_data::SenseData,
26 opcode::Opcode,
27 },
28};
29
30pub trait ZeroCopyType: KnownLayout + Immutable + IntoBytes + ZFromBytes {}
33
34#[inline]
35fn pad_len(n: usize) -> usize {
36 (4 - (n % 4)) % 4
37}
38
39#[inline]
40fn crc32c_of_parts(parts: &[&[u8]]) -> u32 {
41 let mut acc = 0u32;
42 for p in parts {
43 if !p.is_empty() {
44 acc = crc32c_append(acc, p);
45 }
46 }
47 acc
48}
49
50#[inline]
51fn crc32c_with_padding(parts: &[&[u8]], pad: usize) -> u32 {
52 let mut acc = crc32c_of_parts(parts);
53 if pad != 0 {
54 let zeros = [0u8; 3];
55 acc = crc32c_append(acc, &zeros[..pad]);
56 }
57 acc
58}
59
60#[inline]
61fn compute_header_digest(bhs: &[u8], ahs: &[u8]) -> u32 {
62 crc32c_with_padding(&[bhs, ahs], pad_len(ahs.len()))
63}
64
65#[inline]
66fn compute_data_digest(data: &[u8]) -> u32 {
67 crc32c_with_padding(&[data], pad_len(data.len()))
68}
69
70pub type PduRequest<T> = PDUWithData<T, BytesMut>;
72pub type PduResponse<T> = PDUWithData<T, Bytes>;
74
75#[derive(PartialEq)]
81pub struct PDUWithData<T, Body = Bytes> {
82 pub header_buf: [u8; HEADER_LEN],
84 payload: Body,
85
86 enable_header_digest: bool,
87 enable_data_digest: bool,
88 allocated_header_diggest: bool,
89 pub header_digest: Option<U32<BigEndian>>,
91 pub data_digest: Option<U32<BigEndian>>,
93
94 pub is_x86: bool,
95
96 _marker: PhantomData<T>,
97}
98
99impl<T, Body: Clone> Clone for PDUWithData<T, Body> {
100 fn clone(&self) -> Self {
101 Self {
102 header_buf: self.header_buf,
103 payload: self.payload.clone(),
104 enable_header_digest: self.enable_header_digest,
105 enable_data_digest: self.enable_data_digest,
106 allocated_header_diggest: self.allocated_header_diggest,
107 header_digest: self.header_digest,
108 data_digest: self.data_digest,
109 is_x86: self.is_x86,
110 _marker: PhantomData,
111 }
112 }
113}
114
115impl<T> Builder for PDUWithData<T, BytesMut>
116where T: BasicHeaderSegment + SendingData + FromBytes + ZeroCopyType
117{
118 type Body = Bytes;
119 type Header = [u8; HEADER_LEN];
120
121 fn append_data(&mut self, more: &[u8]) {
125 let hd_len = self
126 .header_view()
127 .expect("uninitialized header")
128 .get_header_diggest(self.enable_header_digest);
129 if !self.allocated_header_diggest && hd_len != 0 {
130 self.payload.extend_from_slice(&[0u8; 4][..hd_len]);
131 }
132 self.allocated_header_diggest = true;
133
134 if !more.is_empty() {
135 self.payload.extend_from_slice(more);
136 let old = self
138 .header_view()
139 .expect("header_view failed")
140 .get_data_length_bytes();
141 let new_len = old.saturating_add(more.len()) as u32;
142 self.header_view_mut()
143 .expect("header_view_mut failed")
144 .set_data_length_bytes(new_len);
145 }
146 }
147
148 fn build(
152 &mut self,
153 max_recv_data_segment_length: usize,
154 ) -> Result<(Self::Header, Self::Body)> {
155 let (opcode, ahs_len, data_len, hd_len, dd_len) = {
156 let enable_hd = self.enable_header_digest;
157 let enable_dd = self.enable_data_digest;
158
159 let h = self.header_view_mut().expect("building without header_buf");
160 let opcode = h.get_opcode()?.opcode;
161 h.set_final_bit();
162 let ahs_len = h.get_ahs_length_bytes();
163 let data_len = h.get_data_length_bytes();
164 let hd_len = h.get_header_diggest(enable_hd); let dd_len = h.get_data_diggest(enable_dd); (opcode, ahs_len, data_len, hd_len, dd_len)
167 };
168
169 if data_len > max_recv_data_segment_length {
170 bail!(
171 "MaxRecvDataSegmentLength({max_recv_data_segment_length}) < \
172 data_len({data_len})"
173 );
174 }
175
176 let ahs_pad = pad_len(ahs_len);
178 let data_pad = pad_len(data_len);
179 self.append_data(&[]); self.payload.extend_from_slice(&[0u8; 4][..data_pad]);
181
182 if hd_len != 0 && opcode != Opcode::LoginReq {
183 let hd = compute_header_digest(&self.header_buf, self.additional_header()?);
184 self.header_digest = Some(U32::<BigEndian>::new(hd));
185 let expected_slice = [hd.to_le_bytes(), hd.to_be_bytes()];
186 self.payload
187 .get_mut(0..hd_len)
188 .context("failed to get slice for crc in payload")?
189 .clone_from_slice(&expected_slice[self.is_x86 as usize]);
190 }
191
192 if dd_len != 0 && opcode != Opcode::LoginReq {
195 let dd = compute_data_digest(self.data()?);
196 self.data_digest = Some(U32::<BigEndian>::new(dd));
197 let expected_slice = [dd.to_le_bytes(), dd.to_be_bytes()];
198 self.payload
199 .extend_from_slice(&expected_slice[self.is_x86 as usize]);
200 }
201
202 let expected = ahs_len + ahs_pad + hd_len + data_len + data_pad + dd_len;
203 let actual = self.payload.len();
204 if actual != expected {
205 bail!(
206 "payload size mismatch: actual={}, expected={} (ahs={} padAHS={} hd={} \
207 data={} padDATA={} dd={})",
208 actual,
209 expected,
210 ahs_len,
211 ahs_pad,
212 hd_len,
213 data_len,
214 data_pad,
215 dd_len
216 );
217 }
218
219 let body = self.payload.clone();
220 Ok((self.header_buf, body.freeze()))
221 }
222}
223
224impl<T> PDUWithData<T, Bytes> {
225 pub fn from_header_slice(header_buf: [u8; HEADER_LEN], cfg: &Config) -> Self {
228 Self {
229 header_buf,
230 payload: Bytes::new(),
231 enable_header_digest: cfg.login.integrity.header_digest == Digest::CRC32C,
232 header_digest: None,
233 allocated_header_diggest: false,
234 enable_data_digest: cfg.login.integrity.data_digest == Digest::CRC32C,
235 data_digest: None,
236 is_x86: cfg.login.identity.is_x86 == YesNo::Yes,
237 _marker: PhantomData,
238 }
239 }
240}
241
242impl<T> PDUWithData<T, BytesMut> {
243 pub fn new_request(header_buf: [u8; HEADER_LEN], cfg: &Config) -> Self {
245 Self {
246 header_buf,
247 payload: BytesMut::new(),
248 enable_header_digest: cfg.login.integrity.header_digest == Digest::CRC32C,
249 header_digest: None,
250 allocated_header_diggest: false,
251 enable_data_digest: cfg.login.integrity.data_digest == Digest::CRC32C,
252 data_digest: None,
253 is_x86: cfg.login.identity.is_x86 == YesNo::Yes,
254 _marker: PhantomData,
255 }
256 }
257
258 pub fn parse_with_buff_mut(&mut self, mut buf: BytesMut) -> Result<()>
260 where T: BasicHeaderSegment + FromBytes + ZeroCopyType {
261 let tn = type_name::<T>();
262 let h = self.header_view().context("parsing without header_buf")?;
263
264 let ahs_len = h.get_ahs_length_bytes();
265 let hd_len = h.get_header_diggest(self.enable_header_digest);
266 let data_len = h.get_data_length_bytes();
267 let dd_len = h.get_data_diggest(self.enable_data_digest);
268
269 let ahs_pad = pad_len(ahs_len);
270 let data_pad = pad_len(data_len);
271
272 let need = ahs_len + ahs_pad + hd_len + data_len + data_pad + dd_len;
273 if buf.len() < need {
274 bail!("{tn}: buffer too small: have {}, need {}", buf.len(), need);
275 }
276
277 if buf.len() > need {
278 buf.truncate(need);
279 }
280
281 self.payload = buf;
282
283 let payload: &[u8] = &self.payload;
284
285 let mut off = ahs_len + ahs_pad;
286
287 self.header_digest = if self.enable_header_digest {
288 let expected_slice = payload[off..off + hd_len].try_into()?;
289 let hd = [
290 u32::from_le_bytes(expected_slice),
291 u32::from_be_bytes(expected_slice),
292 ];
293 off += hd_len;
294 Some(U32::<BigEndian>::new(hd[self.is_x86 as usize]))
295 } else {
296 None
297 };
298
299 off += data_len + data_pad;
300
301 self.data_digest = if self.enable_data_digest {
302 let expected_slice = payload[off..off + dd_len].try_into()?;
303 let dd = [
304 u32::from_le_bytes(expected_slice),
305 u32::from_be_bytes(expected_slice),
306 ];
307 Some(U32::<BigEndian>::new(dd[self.is_x86 as usize]))
308 } else {
309 None
310 };
311
312 if self.enable_header_digest {
313 let want = compute_header_digest(&self.header_buf, self.additional_header()?);
314 if self.header_digest.map(|x| x.get()) != Some(want) {
315 bail!("{tn}: HeaderDigest mismatch");
316 }
317 }
318 if self.enable_data_digest {
319 let data = self.data()?;
320 let want = compute_data_digest(data);
321 if !data.is_empty() && self.data_digest.map(|x| x.get()) != Some(want) {
322 bail!("{tn}: DataDigest mismatch");
323 }
324 }
325
326 Ok(())
327 }
328
329 pub fn parse_with_buff_ref(&mut self, buf: &BytesMut) -> Result<()>
331 where T: BasicHeaderSegment + FromBytes + ZeroCopyType {
332 self.parse_with_buff_mut(buf.clone())
333 }
334}
335
336impl<T, B> PDUWithData<T, B>
337where
338 T: BasicHeaderSegment,
339 B: Deref<Target = [u8]>,
340{
341 #[inline]
343 pub fn header_view(&self) -> Result<&T>
344 where T: FromBytes + ZeroCopyType {
345 T::ref_from_bytes(self.header_buf.as_slice()).map_err(|e| anyhow!("{}", e))
346 }
347
348 #[inline]
350 pub fn header_view_mut(&mut self) -> Result<&mut T>
351 where T: FromBytes + ZeroCopyType {
352 T::mut_from_bytes(self.header_buf.as_mut_slice()).map_err(|e| anyhow!("{}", e))
353 }
354
355 pub fn additional_header(&self) -> Result<&[u8]>
357 where T: FromBytes + ZeroCopyType {
358 let ahs_size = self.header_view()?.get_ahs_length_bytes();
359 Ok(&self.payload[0..ahs_size])
360 }
361
362 pub fn data(&self) -> Result<&[u8]>
364 where T: FromBytes + ZeroCopyType {
365 let header = self.header_view()?;
366 let ahs_len = header.get_ahs_length_bytes();
367 let hd = header.get_header_diggest(self.enable_header_digest);
368 let data_len = header.get_data_length_bytes();
369 let total = ahs_len + pad_len(ahs_len) + hd;
370 self.payload
371 .get(total..total + data_len)
372 .context("failed to get slice payload")
373 }
374
375 pub fn rebind_pdu<U>(self) -> anyhow::Result<PDUWithData<U, B>>
377 where U: BasicHeaderSegment {
378 Ok(PDUWithData::<U, B> {
379 header_buf: self.header_buf,
380 payload: self.payload,
381 enable_header_digest: self.enable_header_digest,
382 header_digest: self.header_digest,
383 allocated_header_diggest: self.allocated_header_diggest,
384 enable_data_digest: self.enable_data_digest,
385 data_digest: self.data_digest,
386 is_x86: self.is_x86,
387 _marker: PhantomData,
388 })
389 }
390}
391
392impl<T> PDUWithData<T, Bytes>
393where T: BasicHeaderSegment + FromBytes + ZeroCopyType
394{
395 pub fn parse_with_buff(&mut self, buf: &Bytes) -> Result<()> {
397 let tn = type_name::<T>();
398
399 let h = self.header_view().context("parsing without header_buf")?;
400
401 let ahs_len = h.get_ahs_length_bytes();
402 let hd_len = h.get_header_diggest(self.enable_header_digest);
403 let data_len = h.get_data_length_bytes();
404 let dd_len = h.get_data_diggest(self.enable_data_digest);
405
406 let ahs_pad = pad_len(ahs_len);
407 let data_pad = pad_len(data_len);
408
409 let need = ahs_len + ahs_pad + hd_len + data_len + data_pad + dd_len;
410 if buf.len() < need {
411 bail!("{tn}: buffer too small: have {}, need {}", buf.len(), need);
412 }
413
414 self.payload = buf.clone();
415
416 let mut off = 0usize;
417 off += ahs_len + ahs_pad;
418
419 self.header_digest = if hd_len != 0 {
420 let hd = u32::from_le_bytes(buf[off..off + hd_len].try_into()?);
421 off += hd_len;
422 Some(U32::<BigEndian>::new(hd))
423 } else {
424 None
425 };
426
427 off += data_len + data_pad;
428
429 self.data_digest = if dd_len != 0 {
430 let dd = u32::from_le_bytes(buf[off..off + dd_len].try_into()?);
431 Some(U32::<BigEndian>::new(dd))
432 } else {
433 None
434 };
435
436 if hd_len != 0 {
437 let want = compute_header_digest(&self.header_buf, self.additional_header()?);
438 if self.header_digest.map(|x| x.get()) != Some(want) {
439 bail!("{tn}: HeaderDigest mismatch");
440 }
441 }
442 if dd_len != 0 {
443 let data = self.data()?;
444 let want = compute_data_digest(data);
445 if !data.is_empty() && self.data_digest.map(|x| x.get()) != Some(want) {
446 bail!("{tn}: DataDigest mismatch");
447 }
448 }
449 Ok(())
450 }
451}
452
453struct HexPreview<'a>(&'a [u8]);
457
458impl<'a> fmt::Debug for HexPreview<'a> {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 const MAX: usize = 128;
461 let slice = if self.0.len() > MAX {
462 &self.0[..MAX]
463 } else {
464 self.0
465 };
466 let mut first = true;
467 write!(f, "\"")?;
468 for b in slice {
469 if !first {
470 write!(f, " ")?;
471 }
472 write!(f, "{b:02x}")?;
473 first = false;
474 }
475 if self.0.len() > MAX {
476 write!(f, " ... (+{} bytes)", self.0.len() - MAX)?;
477 }
478 write!(f, "\"")
479 }
480}
481
482impl<T, B> fmt::Debug for PDUWithData<T, B>
483where
484 T: BasicHeaderSegment + SendingData + FromBytes + fmt::Debug + ZeroCopyType,
485 B: Deref<Target = [u8]>,
486{
487 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488 let mut ds = f.debug_struct("PDUWithData");
489 let header = &self.header_view().expect("failed to get header");
490
491 ds.field("header", header);
492
493 let data = &self.data().expect("invlid pdu");
494
495 ds.field("data_len", &data.len());
496
497 match self.header_digest {
498 Some(hd) => ds.field("header_digest", &format_args!("{hd:#010x}")),
499 None => ds.field("header_digest", &r"None"),
500 };
501
502 match self.data_digest {
503 Some(dd) => ds.field("data_digest", &format_args!("{dd:#010x}")),
504 None => ds.field("data_digest", &r"None"),
505 };
506
507 if header.get_opcode().expect("unable to get opcode").opcode
508 == Opcode::ScsiCommandResp
509 && !data.is_empty()
510 {
511 match SenseData::parse(data) {
512 Ok(sense) => {
513 ds.field("sense", &sense);
514 },
515 Err(_e) => {
516 ds.field("data_preview", &HexPreview(data));
517 },
518 }
519 } else if !data.is_empty() {
520 ds.field("data_preview", &HexPreview(data));
521 } else {
522 ds.field("data", &r"[]");
523 }
524
525 ds.finish()
526 }
527}