1use bacnet_npdu::{Address, EncodeError, MAX_MAC_LEN, NetworkMessageType, NetworkPriority};
15use bacnet_types::MacAddr;
16use bacnet_types::error::{Error, RangeError};
17use bytes::{Bytes, BytesMut};
18
19mod decode_error;
20pub use decode_error::NpduDecodeError;
21
22pub use bacnet_npdu as view;
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38pub struct NpduAddress {
39 pub network: u16,
41 pub mac_address: MacAddr,
43}
44
45impl NpduAddress {
46 fn view(&self) -> Address<'_> {
48 Address {
49 network: self.network,
50 mac: &self.mac_address,
51 }
52 }
53}
54
55impl From<Address<'_>> for NpduAddress {
57 fn from(address: Address<'_>) -> Self {
58 Self {
59 network: address.network,
60 mac_address: MacAddr::from_slice(address.mac),
61 }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct Npdu {
75 pub expecting_reply: bool,
77 pub priority: NetworkPriority,
79 pub destination: Option<NpduAddress>,
81 pub source: Option<NpduAddress>,
83 pub hop_count: u8,
87 pub message_type: Option<NetworkMessageType>,
89 pub vendor_id: Option<u16>,
92 pub payload: Bytes,
94}
95
96impl Npdu {
97 pub fn is_network_message(&self) -> bool {
99 self.message_type.is_some()
100 }
101
102 pub fn header_from(view: &view::Npdu<'_>) -> Self {
107 Self {
108 expecting_reply: view.expecting_reply,
109 priority: view.priority,
110 destination: view.destination.map(NpduAddress::from),
111 source: view.source.map(NpduAddress::from),
112 hop_count: view.hop_count,
113 message_type: view.message_type,
114 vendor_id: view.vendor_id,
115 payload: Bytes::new(),
116 }
117 }
118
119 fn view(&self) -> bacnet_npdu::Npdu<'_> {
121 bacnet_npdu::Npdu {
122 expecting_reply: self.expecting_reply,
123 priority: self.priority,
124 destination: self.destination.as_ref().map(NpduAddress::view),
125 source: self.source.as_ref().map(NpduAddress::view),
126 hop_count: self.hop_count,
127 message_type: self.message_type,
128 vendor_id: self.vendor_id,
129 payload: &self.payload,
130 payload_offset: 0,
131 }
132 }
133}
134
135impl Default for Npdu {
136 fn default() -> Self {
137 Self {
138 expecting_reply: false,
139 priority: NetworkPriority::Normal,
140 destination: None,
141 source: None,
142 hop_count: 255,
143 message_type: None,
144 vendor_id: None,
145 payload: Bytes::new(),
146 }
147 }
148}
149
150pub fn encode_npdu(buf: &mut BytesMut, npdu: &Npdu) -> Result<(), Error> {
160 let view = npdu.view();
161 let start = buf.len();
162 buf.resize(start + view.encoded_len(), 0);
163 match view.encode_into(&mut buf[start..]) {
164 Ok(_) => Ok(()),
165 Err(refused) => {
166 buf.truncate(start);
167 Err(encode_error(npdu, refused))
168 }
169 }
170}
171
172fn encode_error(npdu: &Npdu, refused: EncodeError) -> Error {
175 let destination = npdu.destination.as_ref();
176 let source = npdu.source.as_ref();
177 match refused {
178 EncodeError::ReservedNetwork if destination.is_some_and(|d| d.network == 0) => {
179 RangeError::new("NPDU DNET must not be 0")
180 }
181 EncodeError::ReservedNetwork => RangeError::new("NPDU SNET must be 1..65534, got {}")
182 .with_value(source.map_or(0, |s| s.network).into()),
183 EncodeError::EmptySource => RangeError::new("NPDU SLEN must not be 0"),
184 EncodeError::AddressTooLong => {
185 let (label, address) = match destination {
186 Some(d) if d.mac_address.len() > MAX_MAC_LEN => (
187 "NPDU destination address of {} octets exceeds the {}-octet limit",
188 d,
189 ),
190 _ => (
191 "NPDU source address of {} octets exceeds the {}-octet limit",
192 source.expect("an over-long address is one of the two"),
193 ),
194 };
195 RangeError::new(label).with_values(address.mac_address.len() as u64, MAX_MAC_LEN as u64)
196 }
197 _ => RangeError::new("NPDU encoding refused"),
199 }
200 .into()
201}
202
203pub fn decode_npdu(data: Bytes) -> Result<Npdu, NpduDecodeError> {
219 #[cfg(feature = "std")]
221 if let Some(&control) = data.get(1)
222 && control & 0x50 != 0
223 {
224 tracing::warn!(
225 control_byte = control,
226 "NPDU control byte has reserved bits set (bits 4 or 6)"
227 );
228 }
229 let view = match bacnet_npdu::Npdu::decode(&data) {
230 Ok(view) => view,
231 Err(bacnet_npdu::DecodeError::AddressTooLong {
232 field,
233 length,
234 dnet,
235 source,
236 }) => {
237 return Err(NpduDecodeError::AddressTooLong {
238 field,
239 length,
240 dnet,
241 source: source.map(NpduAddress::from),
242 });
243 }
244 Err(bacnet_npdu::DecodeError::Malformed(fault)) => {
245 return Err(NpduDecodeError::Malformed(fault));
246 }
247 };
248 let mut npdu = Npdu::header_from(&view);
249 let offset = view.payload_offset;
252 let mut payload = data;
253 bytes::Buf::advance(&mut payload, offset);
254 npdu.payload = payload;
255 Ok(npdu)
256}
257
258#[cfg(test)]
263mod address_bound_tests;
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use bacnet_npdu::Malformed;
269
270 fn encode_to_vec(npdu: &Npdu) -> Vec<u8> {
271 let mut buf = BytesMut::with_capacity(64);
272 encode_npdu(&mut buf, npdu).unwrap();
273 buf.to_vec()
274 }
275
276 #[test]
277 fn minimal_local_apdu() {
278 let npdu = Npdu {
280 payload: Bytes::from_static(&[0x10, 0x08]), ..Default::default()
282 };
283 let encoded = encode_to_vec(&npdu);
284 assert_eq!(encoded, vec![0x01, 0x00, 0x10, 0x08]);
286
287 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
288 assert_eq!(decoded, npdu);
289 }
290
291 #[test]
292 fn expecting_reply_flag() {
293 let npdu = Npdu {
294 expecting_reply: true,
295 payload: Bytes::from_static(&[0xAA]),
296 ..Default::default()
297 };
298 let encoded = encode_to_vec(&npdu);
299 assert_eq!(encoded[1], 0x04); let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
301 assert!(decoded.expecting_reply);
302 }
303
304 #[test]
305 fn priority_encoding() {
306 for (prio, expected_bits) in [
307 (NetworkPriority::Normal, 0x00),
308 (NetworkPriority::Urgent, 0x01),
309 (NetworkPriority::CriticalEquipment, 0x02),
310 (NetworkPriority::LifeSafety, 0x03),
311 ] {
312 let npdu = Npdu {
313 priority: prio,
314 payload: Bytes::new(),
315 ..Default::default()
316 };
317 let encoded = encode_to_vec(&npdu);
318 assert_eq!(encoded[1] & 0x03, expected_bits);
319 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
320 assert_eq!(decoded.priority, prio);
321 }
322 }
323
324 #[test]
325 fn destination_only_round_trip() {
326 let npdu = Npdu {
327 destination: Some(NpduAddress {
328 network: 1000,
329 mac_address: MacAddr::from_slice(&[0x0A, 0x00, 0x01, 0x01, 0xBA, 0xC0]),
330 }),
331 hop_count: 254,
332 payload: Bytes::from_static(&[0x10, 0x08]),
333 ..Default::default()
334 };
335 let encoded = encode_to_vec(&npdu);
336 assert_eq!(encoded[1] & 0x20, 0x20);
338 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
339 assert_eq!(decoded, npdu);
340 }
341
342 #[test]
343 fn destination_broadcast() {
344 let npdu = Npdu {
346 destination: Some(NpduAddress {
347 network: 0xFFFF,
348 mac_address: MacAddr::new(),
349 }),
350 hop_count: 255,
351 payload: Bytes::from_static(&[0x10, 0x08]),
352 ..Default::default()
353 };
354 let encoded = encode_to_vec(&npdu);
355 assert_eq!(encoded.len(), 8);
357 assert_eq!(&encoded[2..4], &[0xFF, 0xFF]);
359 assert_eq!(encoded[4], 0);
361
362 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
363 assert_eq!(decoded, npdu);
364 }
365
366 #[test]
367 fn source_only_round_trip() {
368 let npdu = Npdu {
369 source: Some(NpduAddress {
370 network: 500,
371 mac_address: MacAddr::from_slice(&[0x01]),
372 }),
373 payload: Bytes::from_static(&[0x30, 0x01, 0x0C]),
374 ..Default::default()
375 };
376 let encoded = encode_to_vec(&npdu);
377 assert_eq!(encoded[1] & 0x08, 0x08);
379 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
381 assert_eq!(decoded, npdu);
382 }
383
384 #[test]
385 fn source_and_destination_round_trip() {
386 let npdu = Npdu {
387 expecting_reply: true,
388 destination: Some(NpduAddress {
389 network: 2000,
390 mac_address: MacAddr::from_slice(&[0x0A, 0x00, 0x02, 0x01, 0xBA, 0xC0]),
391 }),
392 source: Some(NpduAddress {
393 network: 1000,
394 mac_address: MacAddr::from_slice(&[0x0A, 0x00, 0x01, 0x01, 0xBA, 0xC0]),
395 }),
396 hop_count: 250,
397 payload: Bytes::from_static(&[0x00, 0x05, 0x01, 0x0C]),
398 ..Default::default()
399 };
400 let encoded = encode_to_vec(&npdu);
401 assert_eq!(encoded[1], 0x2C);
403 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
404 assert_eq!(decoded, npdu);
405 }
406
407 #[test]
408 fn network_message_round_trip() {
409 let npdu = Npdu {
410 message_type: Some(NetworkMessageType::I_AM_ROUTER_TO_NETWORK),
411 payload: Bytes::from_static(&[0x03, 0xE8]), ..Default::default()
413 };
414 let encoded = encode_to_vec(&npdu);
415 assert_eq!(encoded[1] & 0x80, 0x80);
417 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
418 assert_eq!(decoded, npdu);
419 }
420
421 #[test]
422 fn proprietary_network_message_round_trip() {
423 let npdu = Npdu {
424 message_type: Some(NetworkMessageType::from_raw(0x80)), vendor_id: Some(999),
426 payload: Bytes::from_static(&[0xDE, 0xAD]),
427 ..Default::default()
428 };
429 let encoded = encode_to_vec(&npdu);
430 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
431 assert_eq!(decoded, npdu);
432 }
433
434 #[test]
435 fn wire_format_who_is_broadcast() {
436 let wire = [0x01, 0x20, 0xFF, 0xFF, 0x00, 0xFF, 0x10, 0x08];
440 let decoded = decode_npdu(Bytes::copy_from_slice(&wire)).unwrap();
441 assert!(!decoded.is_network_message());
442 assert!(!decoded.expecting_reply);
443 assert_eq!(decoded.priority, NetworkPriority::Normal);
444 assert_eq!(
445 decoded.destination,
446 Some(NpduAddress {
447 network: 0xFFFF,
448 mac_address: MacAddr::new(),
449 })
450 );
451 assert!(decoded.source.is_none());
452 assert_eq!(decoded.hop_count, 255);
453 assert_eq!(decoded.payload, vec![0x10, 0x08]);
454
455 let reencoded = encode_to_vec(&decoded);
457 assert_eq!(reencoded, wire);
458 }
459
460 #[test]
461 fn decode_too_short() {
462 assert_eq!(
463 decode_npdu(Bytes::new()),
464 Err(NpduDecodeError::Malformed(Malformed::Truncated))
465 );
466 assert_eq!(
467 decode_npdu(Bytes::from_static(&[0x01])),
468 Err(NpduDecodeError::Malformed(Malformed::Truncated))
469 );
470 }
471
472 #[test]
473 fn decode_wrong_version() {
474 assert_eq!(
475 decode_npdu(Bytes::from_static(&[0x02, 0x00])),
476 Err(NpduDecodeError::Malformed(Malformed::Version))
477 );
478 }
479
480 #[test]
481 fn decode_truncated_destination() {
482 assert_eq!(
484 decode_npdu(Bytes::from_static(&[0x01, 0x20, 0xFF])),
485 Err(NpduDecodeError::Malformed(Malformed::Truncated))
486 );
487 }
488
489 #[test]
490 fn decode_truncated_source() {
491 assert_eq!(
493 decode_npdu(Bytes::from_static(&[0x01, 0x08, 0x00])),
494 Err(NpduDecodeError::Malformed(Malformed::Truncated))
495 );
496 }
497
498 #[test]
501 fn npdu_network_zero() {
502 let npdu = Npdu {
504 destination: Some(NpduAddress {
505 network: 0,
506 mac_address: MacAddr::from_slice(&[0x01]),
507 }),
508 hop_count: 255,
509 payload: Bytes::from_static(&[0x10, 0x08]),
510 ..Default::default()
511 };
512 let mut buf = BytesMut::new();
513 let result = encode_npdu(&mut buf, &npdu);
514 assert!(result.is_err());
515 let err = format!("{}", result.unwrap_err());
516 assert!(err.contains("DNET"), "got: {err}");
517 }
518
519 #[test]
520 fn npdu_network_fffe() {
521 let npdu = Npdu {
524 destination: Some(NpduAddress {
525 network: 0xFFFE,
526 mac_address: MacAddr::from_slice(&[0x01, 0x02]),
527 }),
528 hop_count: 200,
529 payload: Bytes::from_static(&[0xAA]),
530 ..Default::default()
531 };
532 let encoded = encode_to_vec(&npdu);
533 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
534 assert_eq!(decoded.destination.as_ref().unwrap().network, 0xFFFE);
535 assert_eq!(decoded.hop_count, 200);
536 }
537
538 #[test]
539 fn npdu_hop_count_zero() {
540 let npdu = Npdu {
542 destination: Some(NpduAddress {
543 network: 1000,
544 mac_address: MacAddr::new(),
545 }),
546 hop_count: 0,
547 payload: Bytes::from_static(&[0x10, 0x08]),
548 ..Default::default()
549 };
550 let encoded = encode_to_vec(&npdu);
551 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
552 assert_eq!(decoded.hop_count, 0);
553 }
554
555 #[test]
556 fn npdu_source_with_empty_mac() {
557 let npdu = Npdu {
559 source: Some(NpduAddress {
560 network: 500,
561 mac_address: MacAddr::new(),
562 }),
563 payload: Bytes::from_static(&[0xBB]),
564 ..Default::default()
565 };
566 let mut buf = BytesMut::new();
567 let result = encode_npdu(&mut buf, &npdu);
568 assert!(result.is_err());
569 let err = format!("{}", result.unwrap_err());
570 assert!(err.contains("SLEN"), "got: {err}");
571 }
572
573 #[test]
574 fn npdu_destination_dlen_zero_broadcast_accepted() {
575 let npdu = Npdu {
577 destination: Some(NpduAddress {
578 network: 0xFFFF,
579 mac_address: MacAddr::new(),
580 }),
581 hop_count: 255,
582 payload: Bytes::from_static(&[0x10, 0x08]),
583 ..Default::default()
584 };
585 let encoded = encode_to_vec(&npdu);
586 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
587 assert_eq!(decoded.destination.as_ref().unwrap().network, 0xFFFF);
588 assert!(decoded.destination.as_ref().unwrap().mac_address.is_empty());
589 }
590
591 #[test]
592 fn npdu_destination_truncated_mac() {
593 let data = [0x01, 0x20, 0x03, 0xE8, 0x06, 0x01, 0x02];
596 assert_eq!(
597 decode_npdu(Bytes::copy_from_slice(&data)),
598 Err(NpduDecodeError::Malformed(Malformed::Truncated))
599 );
600 }
601
602 #[test]
603 fn npdu_source_truncated_mac() {
604 let data = [0x01, 0x08, 0x01, 0xF4, 0x04, 0x01]; assert_eq!(
607 decode_npdu(Bytes::copy_from_slice(&data)),
608 Err(NpduDecodeError::Malformed(Malformed::Truncated))
609 );
610 }
611
612 #[test]
613 fn npdu_missing_hop_count() {
614 let data = [0x01, 0x20, 0xFF, 0xFF, 0x00];
617 assert_eq!(
618 decode_npdu(Bytes::copy_from_slice(&data)),
619 Err(NpduDecodeError::Malformed(Malformed::Truncated))
620 );
621 }
622
623 #[test]
624 fn npdu_network_message_truncated_type() {
625 let data = [0x01, 0x80]; assert_eq!(
628 decode_npdu(Bytes::copy_from_slice(&data)),
629 Err(NpduDecodeError::Malformed(Malformed::Truncated))
630 );
631 }
632
633 #[test]
634 fn npdu_proprietary_message_truncated_vendor() {
635 let data = [0x01, 0x80, 0x80]; assert_eq!(
638 decode_npdu(Bytes::copy_from_slice(&data)),
639 Err(NpduDecodeError::Malformed(Malformed::Truncated))
640 );
641 }
642
643 #[test]
644 fn npdu_all_flags_round_trip() {
645 let npdu = Npdu {
647 expecting_reply: true,
648 priority: NetworkPriority::LifeSafety,
649 destination: Some(NpduAddress {
650 network: 2000,
651 mac_address: MacAddr::from_slice(&[0x0A, 0x00, 0x02, 0x01, 0xBA, 0xC0]),
652 }),
653 source: Some(NpduAddress {
654 network: 1000,
655 mac_address: MacAddr::from_slice(&[0x0A, 0x00, 0x01, 0x01, 0xBA, 0xC0]),
656 }),
657 hop_count: 127,
658 payload: Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF]),
659 ..Default::default()
660 };
661 let encoded = encode_to_vec(&npdu);
662 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
663 assert_eq!(decoded, npdu);
664 }
665
666 #[test]
667 fn npdu_empty_payload() {
668 let npdu = Npdu {
670 payload: Bytes::new(),
671 ..Default::default()
672 };
673 let encoded = encode_to_vec(&npdu);
674 let decoded = decode_npdu(Bytes::from(encoded)).unwrap();
675 assert!(decoded.payload.is_empty());
676 }
677
678 #[test]
679 fn reject_snet_zero() {
680 let npdu = Npdu {
682 source: Some(NpduAddress {
683 network: 0,
684 mac_address: MacAddr::from_slice(&[0x01]),
685 }),
686 payload: Bytes::from_static(&[0x10, 0x08]),
687 ..Default::default()
688 };
689 let mut buf = BytesMut::new();
690 let result = encode_npdu(&mut buf, &npdu);
691 assert!(result.is_err());
692 let err = format!("{}", result.unwrap_err());
693 assert!(err.contains("SNET"), "got: {err}");
694 }
695
696 #[test]
697 fn reserved_bits_warning_still_decodes() {
698 let mut data = vec![0x01, 0x40]; data.extend_from_slice(&[0x10, 0x08]);
702
703 let result = decode_npdu(Bytes::copy_from_slice(&data));
705 assert!(
706 result.is_ok(),
707 "reserved bits should not cause decode failure"
708 );
709 }
710}