1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use Result;
use enum_dispatch;
use crate::;
pub const HEADER_LEN: usize = 48;
/// Common helper-trait for PDUs that may be fragmented into several
/// wire-frames (RFC 7143 ― “F”/“C” bits).
///
/// *Most* iSCSI PDUs are transferred in a single frame, but a few
/// (Text, Login, SCSI Command/Data, …) allow the sender to split the
/// **Data-Segment** into a sequence of chunks whose order is determined
/// by the transport; the target relies only on the *Continue* and *Final*
/// flags found in byte 1 of every Basic-Header-Segment.
///
/// Implementing `SendingData` lets generic helpers (e.g. the
/// `PDUWithData` builder or the `Connection` read-loop) toggle and query
/// those flags **without** knowing the concrete PDU type.
///
/// ### Contract
///
/// | method | meaning in sender’s point of view | must keep invariant |
/// |-----------------------|------------------------------------------------------------------------|-------------------------------------------|
/// | `get_final_bit()` | `true` ⇒ this frame is the **last** one of the sequence | |
/// | `set_final_bit()` | set *F = 1* **and** (if applicable) clear *C = 1* | after call, `get_final_bit()==true` |
/// | `get_continue_bit()` | `true` ⇒ **at least one** more frame will follow | |
/// | `set_continue_bit()` | clear *F* and set *C* so that the receiver expects more frames | after call, `get_continue_bit()==true` |
///
/// ### Notes
///
/// * A well-formed PDU **cannot** have both *F=1* **and** *C=1*.
/// Implementations should enforce that when toggling the flags.
/// * PDUs that are always single-framed (e.g. NOP-In/Out) may implement these
/// methods as *no-ops* or even panic if someone attempts to change the flag.
/// * The trait is kept separate from `BasicHeaderSegment` so that it can also
/// be used for helper wrappers that are not themselves BHS types (e.g.
/// composite builders).
/// Common functionality for any iSCSI PDU “Basic Header Segment” (BHS).
///
/// A BHS is always 48 bytes long; higher‐level PDUs then may
/// carry additional AHS sections, a variable-length DataSegment,
/// and optional digests. This trait encapsulates:
/// 1. extracting lengths out of the BHS,
/// 2. appending to the DataSegment,
/// 3. and finally building the full wire format.
/// A helper-trait for **builder objects** that construct a complete
/// iSCSI PDU: a 48-byte Basic-Header-Segment (BHS) plus the optional
/// **Data-Segment** and digests.
///
/// The concrete type that implements `Builder` usually owns a
/// *(header + payload)* pair and offers additional, PDU-specific setter
/// methods (e.g. `.lun( … )`, `.read()`, …).
///
/// When your application is ready to send the packet you call
/// [`Builder::build`]; the helper splits the payload into chunks that
/// respect *MaxRecvDataSegmentLength* and automatically toggles the
/// **F/C** bits on the header copies.
///
/// ### Associated type
///
/// * `Header` — the *encoded* header bytes returned by `build`. For most
/// builders this will be `Vec<u8>` or `[u8; 48]`.
///
/// ### Required methods
///
/// * **`append_data(&mut self, more)`** Extends the internal payload buffer
/// with `more` and updates the `DataSegmentLength` field inside the owned
/// header **immediately**. The method mutates `self`; it does **not** return
/// a new builder.
///
/// * **`build(&mut self, cfg)`** Consumes the builder and returns a vector of
/// `(header, body)` tuples. Each tuple represents **one** wire-frame to be
/// written:
///
/// 1. The header slice’s length is always 48 bytes.
/// 2. The payload slice may be empty (`Vec::new()`) for PDUs that carry only
/// a header.
/// 3. The method splits the payload into
/// `cfg.login.negotiation.max_recv_data_segment_length` sized chunks and
/// sets the **Continue** / **Final** flags accordingly.
///
/// The caller is expected to serialise the returned frames *in order*.
///
/// ### Example
///
/// ```no_run
/// # use anyhow::Result;
/// # use iscsi_client_rs::models::{common::Builder, command::request::ScsiCommandRequestBuilder};
/// # use iscsi_client_rs::cfg::config::Config;
/// # fn send(cfg: &Config) -> Result<()> {
/// let mut req = ScsiCommandRequestBuilder::new()
/// .lun(&[0;8])
/// .read()
/// .finall();
///
/// req.append_data(vec![0xde, 0xad, 0xbe, 0xef]);
///
/// for (hdr, body) in req.build(cfg)? {
/// // socket.write_all(&hdr).await?;
/// // socket.write_all(&body).await?;
/// }
/// # Ok(())
/// # }
/// ```