1use crate::mesh_artifact_types::bad;
19pub use crate::mesh_artifact_types::{
20 ArtifactDescriptor, ArtifactId, ArtifactKey, Bitfield, DEFAULT_BLOCK_SIZE, DEFAULT_PIECE_SIZE,
21 MAX_ARTIFACT_FRAME, MAX_PIECE_SIZE, MAX_REASON_BYTES, MAX_REQUEST_PIECES, MIN_PIECE_SIZE,
22 WireError,
23};
24use crate::mesh_framing::{FrameType, MeshFrame};
25
26const TAG_META_REQUEST: u8 = 0x01;
27const TAG_ANNOUNCE: u8 = 0x02;
28const TAG_REQUEST: u8 = 0x03;
29const TAG_META: u8 = 0x11;
30const TAG_PIECE: u8 = 0x12;
31const TAG_NO_PIECE: u8 = 0x13;
32const TAG_REJECT: u8 = 0x14;
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum ArtifactMsg {
37 MetaRequest {
39 key: ArtifactKey,
41 },
42 Announce {
44 id: ArtifactId,
46 have: Bitfield,
48 },
49 Request {
51 id: ArtifactId,
53 pieces: Vec<u32>,
55 },
56 Meta {
58 descriptor: ArtifactDescriptor,
60 },
61 Piece {
63 id: ArtifactId,
65 index: u32,
67 offset: u64,
69 data: Vec<u8>,
71 },
72 NoPiece {
74 id: ArtifactId,
76 index: u32,
78 },
79 Reject {
81 key: ArtifactKey,
83 reason: String,
85 },
86}
87
88impl ArtifactMsg {
89 pub fn frame_type(&self) -> FrameType {
91 match self {
92 Self::MetaRequest { .. } | Self::Announce { .. } | Self::Request { .. } => {
93 FrameType::ArtifactRequest
94 }
95 _ => FrameType::ArtifactResponse,
96 }
97 }
98
99 pub fn to_frame(&self) -> Result<MeshFrame, WireError> {
101 let mut w = Vec::new();
102 match self {
103 Self::MetaRequest { key } => {
104 w.push(TAG_META_REQUEST);
105 put_key(&mut w, key);
106 }
107 Self::Announce { id, have } => {
108 w.push(TAG_ANNOUNCE);
109 w.extend_from_slice(&id.0);
110 w.extend_from_slice(&have.len.to_be_bytes());
111 w.extend_from_slice(&have.bits);
112 }
113 Self::Request { id, pieces } => {
114 if pieces.len() > MAX_REQUEST_PIECES {
115 return Err(bad("too many pieces in one request"));
116 }
117 w.push(TAG_REQUEST);
118 w.extend_from_slice(&id.0);
119 w.extend_from_slice(&(pieces.len() as u32).to_be_bytes());
120 for p in pieces {
121 w.extend_from_slice(&p.to_be_bytes());
122 }
123 }
124 Self::Meta { descriptor: d } => {
125 w.push(TAG_META);
126 w.extend_from_slice(&d.piece_size.to_be_bytes());
127 w.extend_from_slice(&d.total_size.to_be_bytes());
128 w.extend_from_slice(&d.content_hash);
129 w.extend_from_slice(&(d.pieces.len() as u32).to_be_bytes());
130 for p in &d.pieces {
131 w.extend_from_slice(p);
132 }
133 }
134 Self::Piece {
135 id,
136 index,
137 offset,
138 data,
139 } => {
140 w.reserve(1 + 32 + 4 + 8 + 4 + data.len());
141 w.push(TAG_PIECE);
142 w.extend_from_slice(&id.0);
143 w.extend_from_slice(&index.to_be_bytes());
144 w.extend_from_slice(&offset.to_be_bytes());
145 w.extend_from_slice(&(data.len() as u32).to_be_bytes());
146 w.extend_from_slice(data);
147 }
148 Self::NoPiece { id, index } => {
149 w.push(TAG_NO_PIECE);
150 w.extend_from_slice(&id.0);
151 w.extend_from_slice(&index.to_be_bytes());
152 }
153 Self::Reject { key, reason } => {
154 w.push(TAG_REJECT);
155 put_key(&mut w, key);
156 let r = truncate_utf8(reason, MAX_REASON_BYTES);
157 w.extend_from_slice(&(r.len() as u16).to_be_bytes());
158 w.extend_from_slice(r.as_bytes());
159 }
160 }
161 if w.len() > MAX_ARTIFACT_FRAME {
162 return Err(WireError::FrameTooLarge {
163 size: w.len(),
164 max: MAX_ARTIFACT_FRAME,
165 });
166 }
167 Ok(MeshFrame {
168 frame_type: self.frame_type(),
169 payload: w,
170 })
171 }
172
173 pub fn to_wire(&self) -> Result<Vec<u8>, WireError> {
175 self.to_frame()?
176 .encode()
177 .map_err(|e| bad(format!("frame encode: {e}")))
178 }
179
180 pub fn from_wire(raw: &[u8]) -> Result<Self, WireError> {
183 if raw.len() < 5 {
184 return Err(bad("short frame"));
185 }
186 let declared = u32::from_be_bytes([raw[0], raw[1], raw[2], raw[3]]) as usize;
187 let payload_len = declared.saturating_sub(1).max(raw.len().saturating_sub(5));
188 if payload_len > MAX_ARTIFACT_FRAME {
189 return Err(WireError::FrameTooLarge {
190 size: payload_len,
191 max: MAX_ARTIFACT_FRAME,
192 });
193 }
194 if declared != raw.len() - 4 {
195 return Err(bad("length prefix does not match frame"));
196 }
197 let frame = MeshFrame::decode(&raw[4..]).map_err(|e| bad(e.to_string()))?;
198 Self::from_frame(&frame)
199 }
200
201 pub fn from_frame(frame: &MeshFrame) -> Result<Self, WireError> {
203 if frame.payload.len() > MAX_ARTIFACT_FRAME {
204 return Err(WireError::FrameTooLarge {
205 size: frame.payload.len(),
206 max: MAX_ARTIFACT_FRAME,
207 });
208 }
209 let mut r = Reader(&frame.payload);
210 let tag = r.u8()?;
211 let msg = match tag {
212 TAG_META_REQUEST => Self::MetaRequest { key: r.key()? },
213 TAG_ANNOUNCE => {
214 let id = ArtifactId(r.hash()?);
215 let len = r.u32()?;
216 let bits = r.take((len as usize).div_ceil(8))?.to_vec();
217 Self::Announce {
218 id,
219 have: Bitfield { len, bits },
220 }
221 }
222 TAG_REQUEST => {
223 let id = ArtifactId(r.hash()?);
224 let n = r.u32()? as usize;
225 if n > MAX_REQUEST_PIECES {
226 return Err(bad("too many pieces in one request"));
227 }
228 let pieces = (0..n).map(|_| r.u32()).collect::<Result<_, _>>()?;
229 Self::Request { id, pieces }
230 }
231 TAG_META => {
232 let piece_size = r.u64()?;
233 let total_size = r.u64()?;
234 let content_hash = r.hash()?;
235 let n = r.u32()? as usize;
236 if n.saturating_mul(32) > r.0.len() {
237 return Err(bad("piece list longer than frame"));
238 }
239 let pieces = (0..n).map(|_| r.hash()).collect::<Result<_, _>>()?;
240 let descriptor = ArtifactDescriptor {
241 piece_size,
242 total_size,
243 content_hash,
244 pieces,
245 };
246 descriptor.validate()?;
247 Self::Meta { descriptor }
248 }
249 TAG_PIECE => {
250 let id = ArtifactId(r.hash()?);
251 let index = r.u32()?;
252 let offset = r.u64()?;
253 let n = r.u32()? as usize;
254 let data = r.take(n)?.to_vec();
255 Self::Piece {
256 id,
257 index,
258 offset,
259 data,
260 }
261 }
262 TAG_NO_PIECE => Self::NoPiece {
263 id: ArtifactId(r.hash()?),
264 index: r.u32()?,
265 },
266 TAG_REJECT => {
267 let key = r.key()?;
268 let n = r.u16()? as usize;
269 if n > MAX_REASON_BYTES {
270 return Err(bad("reject reason too long"));
271 }
272 let reason = String::from_utf8(r.take(n)?.to_vec())
273 .map_err(|_| bad("reject reason not utf-8"))?;
274 Self::Reject { key, reason }
275 }
276 other => return Err(bad(format!("unknown artifact message tag 0x{other:02x}"))),
277 };
278 if !r.0.is_empty() {
279 return Err(bad("trailing bytes after artifact message"));
280 }
281 if msg.frame_type() != frame.frame_type {
282 return Err(bad("artifact message in the wrong frame type"));
283 }
284 Ok(msg)
285 }
286}
287
288fn put_key(w: &mut Vec<u8>, key: &ArtifactKey) {
289 match key {
290 ArtifactKey::Root(id) => {
291 w.push(0);
292 w.extend_from_slice(&id.0);
293 }
294 ArtifactKey::Content(h) => {
295 w.push(1);
296 w.extend_from_slice(h);
297 }
298 }
299}
300
301fn truncate_utf8(s: &str, max: usize) -> &str {
302 if s.len() <= max {
303 return s;
304 }
305 let mut end = max;
306 while !s.is_char_boundary(end) {
307 end -= 1;
308 }
309 &s[..end]
310}
311
312struct Reader<'a>(&'a [u8]);
313
314impl<'a> Reader<'a> {
315 fn take(&mut self, n: usize) -> Result<&'a [u8], WireError> {
316 if n > self.0.len() {
317 return Err(bad("truncated artifact message"));
318 }
319 let (head, tail) = self.0.split_at(n);
320 self.0 = tail;
321 Ok(head)
322 }
323 fn u8(&mut self) -> Result<u8, WireError> {
324 Ok(self.take(1)?[0])
325 }
326 fn u16(&mut self) -> Result<u16, WireError> {
327 let b = self.take(2)?;
328 Ok(u16::from_be_bytes([b[0], b[1]]))
329 }
330 fn u32(&mut self) -> Result<u32, WireError> {
331 let b = self.take(4)?;
332 Ok(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
333 }
334 fn u64(&mut self) -> Result<u64, WireError> {
335 let mut a = [0u8; 8];
336 a.copy_from_slice(self.take(8)?);
337 Ok(u64::from_be_bytes(a))
338 }
339 fn hash(&mut self) -> Result<[u8; 32], WireError> {
340 let mut a = [0u8; 32];
341 a.copy_from_slice(self.take(32)?);
342 Ok(a)
343 }
344 fn key(&mut self) -> Result<ArtifactKey, WireError> {
345 match self.u8()? {
346 0 => Ok(ArtifactKey::Root(ArtifactId(self.hash()?))),
347 1 => Ok(ArtifactKey::Content(self.hash()?)),
348 k => Err(bad(format!("unknown key kind {k}"))),
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 fn id() -> ArtifactId {
358 ArtifactId([7; 32])
359 }
360
361 fn roundtrip(m: ArtifactMsg) {
362 let raw = m.to_wire().unwrap();
363 assert_eq!(ArtifactMsg::from_wire(&raw).unwrap(), m);
364 }
365
366 #[test]
367 fn every_message_roundtrips_in_its_frame_type() {
368 let mut have = Bitfield::new(11);
369 have.set(0, true);
370 have.set(10, true);
371 let d = ArtifactDescriptor {
372 piece_size: 1024,
373 total_size: 2048 + 5,
374 content_hash: [1; 32],
375 pieces: vec![[2; 32], [3; 32], [4; 32]],
376 };
377 roundtrip(ArtifactMsg::MetaRequest {
378 key: ArtifactKey::Content([9; 32]),
379 });
380 roundtrip(ArtifactMsg::Announce { id: id(), have });
381 roundtrip(ArtifactMsg::Request {
382 id: id(),
383 pieces: vec![0, 3, 9],
384 });
385 roundtrip(ArtifactMsg::Meta { descriptor: d });
386 roundtrip(ArtifactMsg::Piece {
387 id: id(),
388 index: 2,
389 offset: 4096,
390 data: vec![5; 100],
391 });
392 roundtrip(ArtifactMsg::NoPiece { id: id(), index: 1 });
393 roundtrip(ArtifactMsg::Reject {
394 key: ArtifactKey::Root(id()),
395 reason: "not servable".into(),
396 });
397 let req = ArtifactMsg::Request {
398 id: id(),
399 pieces: vec![],
400 };
401 assert_eq!(req.frame_type().as_byte(), 0x0B);
402 let piece = ArtifactMsg::NoPiece { id: id(), index: 0 };
403 assert_eq!(piece.frame_type().as_byte(), 0x0C);
404 }
405
406 #[test]
407 fn oversize_frames_are_refused_both_ways() {
408 let big = ArtifactMsg::Piece {
409 id: id(),
410 index: 0,
411 offset: 0,
412 data: vec![0; MAX_ARTIFACT_FRAME],
413 };
414 assert!(matches!(
415 big.to_wire(),
416 Err(WireError::FrameTooLarge { .. })
417 ));
418 let mut raw = vec![];
420 raw.extend_from_slice(&((MAX_ARTIFACT_FRAME + 2) as u32).to_be_bytes());
421 raw.push(0x0C);
422 raw.extend_from_slice(&[0; 64]);
423 assert!(matches!(
424 ArtifactMsg::from_wire(&raw),
425 Err(WireError::FrameTooLarge { .. })
426 ));
427 }
428
429 #[test]
430 fn malformed_frames_are_refused() {
431 let mut raw = ArtifactMsg::NoPiece { id: id(), index: 1 }
432 .to_wire()
433 .unwrap();
434 raw[4] = 0x0B;
436 assert!(ArtifactMsg::from_wire(&raw).is_err());
437 let mut raw = ArtifactMsg::NoPiece { id: id(), index: 1 }
439 .to_wire()
440 .unwrap();
441 raw.push(0);
442 assert!(ArtifactMsg::from_wire(&raw).is_err());
443 let d = ArtifactDescriptor {
445 piece_size: 1024,
446 total_size: 10_000,
447 content_hash: [0; 32],
448 pieces: vec![[0; 32]],
449 };
450 let raw = ArtifactMsg::Meta { descriptor: d }.to_wire().unwrap();
451 assert!(ArtifactMsg::from_wire(&raw).is_err());
452 }
453}