1use std::time::Duration;
2
3use bytes::Bytes;
4use ndn_tlv::{TlvReader, TlvWriter};
5
6use super::{next_nonce, nni, rand_nonce_bytes, write_name, write_nni};
7use crate::{Name, SignatureType, tlv_type};
8
9pub fn encode_interest(name: &Name, app_params: Option<&[u8]>) -> Bytes {
23 let mut w = TlvWriter::new();
24 w.write_nested(tlv_type::INTEREST, |w| {
25 if let Some(params) = app_params {
26 let mut params_tlv = TlvWriter::new();
29 params_tlv.write_tlv(tlv_type::APP_PARAMETERS, params);
30 let params_wire = params_tlv.finish();
31 let digest = ring::digest::digest(&ring::digest::SHA256, ¶ms_wire);
32
33 w.write_nested(tlv_type::NAME, |w| {
35 for comp in name.components() {
36 w.write_tlv(comp.typ, &comp.value);
37 }
38 w.write_tlv(tlv_type::PARAMETERS_SHA256, digest.as_ref());
39 });
40 w.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
41 write_nni(w, tlv_type::INTEREST_LIFETIME, 4000);
42 w.write_tlv(tlv_type::APP_PARAMETERS, params);
43 } else {
44 write_name(w, name);
45 w.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
46 write_nni(w, tlv_type::INTEREST_LIFETIME, 4000);
47 }
48 });
49 w.finish()
50}
51
52pub fn ensure_nonce(interest_wire: &Bytes) -> Bytes {
60 let mut reader = TlvReader::new(interest_wire.clone());
62 let Ok((typ, value)) = reader.read_tlv() else {
63 return interest_wire.clone();
64 };
65 if typ != tlv_type::INTEREST {
66 return interest_wire.clone();
67 }
68
69 let mut inner = TlvReader::new(value.clone());
70 while !inner.is_empty() {
71 let Ok((t, _)) = inner.read_tlv() else { break };
72 if t == tlv_type::NONCE {
73 return interest_wire.clone(); }
75 }
76
77 let mut w = TlvWriter::new();
79 w.write_nested(tlv_type::INTEREST, |w| {
80 let mut inner = TlvReader::new(value);
81 let mut name_written = false;
82 while !inner.is_empty() {
83 let Ok((t, v)) = inner.read_tlv() else { break };
84 w.write_tlv(t, &v);
85 if !name_written && t == tlv_type::NAME {
87 w.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
88 name_written = true;
89 }
90 }
91 if !name_written {
92 w.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
94 }
95 });
96 w.finish()
97}
98
99pub struct InterestBuilder {
112 name: Name,
113 lifetime: Option<Duration>,
114 can_be_prefix: bool,
115 must_be_fresh: bool,
116 hop_limit: Option<u8>,
117 app_parameters: Option<Vec<u8>>,
118 forwarding_hint: Option<Vec<Name>>,
119}
120
121impl InterestBuilder {
122 pub fn new(name: impl Into<Name>) -> Self {
123 Self {
124 name: name.into(),
125 lifetime: None,
126 can_be_prefix: false,
127 must_be_fresh: false,
128 hop_limit: None,
129 app_parameters: None,
130 forwarding_hint: None,
131 }
132 }
133
134 pub fn lifetime(mut self, d: Duration) -> Self {
135 self.lifetime = Some(d);
136 self
137 }
138
139 pub fn can_be_prefix(mut self) -> Self {
140 self.can_be_prefix = true;
141 self
142 }
143
144 pub fn must_be_fresh(mut self) -> Self {
145 self.must_be_fresh = true;
146 self
147 }
148
149 pub fn hop_limit(mut self, h: u8) -> Self {
150 self.hop_limit = Some(h);
151 self
152 }
153
154 pub fn app_parameters(mut self, p: impl Into<Vec<u8>>) -> Self {
155 self.app_parameters = Some(p.into());
156 self
157 }
158
159 pub fn forwarding_hint(mut self, names: Vec<Name>) -> Self {
160 self.forwarding_hint = Some(names);
161 self
162 }
163
164 pub fn build_with_timeout(self) -> (Bytes, std::time::Duration) {
180 let lifetime = self
181 .lifetime
182 .unwrap_or(std::time::Duration::from_millis(4000));
183 let timeout = lifetime + std::time::Duration::from_millis(500);
184 (self.build(), timeout)
185 }
186
187 pub fn build(self) -> Bytes {
188 let lifetime_ms = self.lifetime.map(|d| d.as_millis() as u64).unwrap_or(4000);
189
190 let mut w = TlvWriter::new();
191 w.write_nested(tlv_type::INTEREST, |w| {
192 if let Some(ref params) = self.app_parameters {
193 let mut params_tlv = TlvWriter::new();
195 params_tlv.write_tlv(tlv_type::APP_PARAMETERS, params);
196 let params_wire = params_tlv.finish();
197 let digest = ring::digest::digest(&ring::digest::SHA256, ¶ms_wire);
198
199 w.write_nested(tlv_type::NAME, |w| {
200 for comp in self.name.components() {
201 w.write_tlv(comp.typ, &comp.value);
202 }
203 w.write_tlv(tlv_type::PARAMETERS_SHA256, digest.as_ref());
204 });
205 } else {
206 write_name(w, &self.name);
207 }
208 if self.can_be_prefix {
209 w.write_tlv(tlv_type::CAN_BE_PREFIX, &[]);
210 }
211 if self.must_be_fresh {
212 w.write_tlv(tlv_type::MUST_BE_FRESH, &[]);
213 }
214 if let Some(ref hints) = self.forwarding_hint {
215 w.write_nested(tlv_type::FORWARDING_HINT, |w| {
216 for h in hints {
217 write_name(w, h);
218 }
219 });
220 }
221 w.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
222 write_nni(w, tlv_type::INTEREST_LIFETIME, lifetime_ms);
223 if let Some(h) = self.hop_limit {
224 w.write_tlv(tlv_type::HOP_LIMIT, &[h]);
225 }
226 if let Some(ref params) = self.app_parameters {
227 w.write_tlv(tlv_type::APP_PARAMETERS, params);
228 }
229 });
230 w.finish()
231 }
232
233 pub async fn sign<F, Fut>(
248 self,
249 sig_type: SignatureType,
250 key_locator: Option<&Name>,
251 sign_fn: F,
252 ) -> Bytes
253 where
254 F: FnOnce(&[u8]) -> Fut,
255 Fut: std::future::Future<Output = Bytes>,
256 {
257 let (mut signed_region, digest_value_offset, app_params_offset) =
258 self.build_signed_interest_region(sig_type, key_locator);
259 let sig_value = sign_fn(&signed_region).await;
260
261 let mut sigval_w = TlvWriter::new();
263 sigval_w.write_tlv(tlv_type::INTEREST_SIGNATURE_VALUE, &sig_value);
264 let sigval_bytes = sigval_w.finish();
265
266 let mut digest_input =
268 Vec::with_capacity((signed_region.len() - app_params_offset) + sigval_bytes.len());
269 digest_input.extend_from_slice(&signed_region[app_params_offset..]);
270 digest_input.extend_from_slice(&sigval_bytes);
271 let actual_digest = ring::digest::digest(&ring::digest::SHA256, &digest_input);
272
273 signed_region[digest_value_offset..digest_value_offset + 32]
275 .copy_from_slice(actual_digest.as_ref());
276
277 let inner_len = signed_region.len() + sigval_bytes.len();
278 let mut outer = TlvWriter::with_capacity(inner_len + 10);
279 outer.write_varu64(tlv_type::INTEREST);
280 outer.write_varu64(inner_len as u64);
281 outer.write_raw(&signed_region);
282 outer.write_raw(&sigval_bytes);
283 outer.finish()
284 }
285
286 #[cfg(feature = "std")]
295 pub fn sign_digest_sha256(self) -> Bytes {
296 self.sign_sync(SignatureType::DigestSha256, None, |region| {
297 let digest = ring::digest::digest(&ring::digest::SHA256, region);
298 Bytes::copy_from_slice(digest.as_ref())
299 })
300 }
301
302 pub fn sign_sync<F>(
304 self,
305 sig_type: SignatureType,
306 key_locator: Option<&Name>,
307 sign_fn: F,
308 ) -> Bytes
309 where
310 F: FnOnce(&[u8]) -> Bytes,
311 {
312 let (mut signed_region, digest_value_offset, app_params_offset) =
313 self.build_signed_interest_region(sig_type, key_locator);
314 let sig_value = sign_fn(&signed_region);
315
316 let mut sigval_w = TlvWriter::new();
318 sigval_w.write_tlv(tlv_type::INTEREST_SIGNATURE_VALUE, &sig_value);
319 let sigval_bytes = sigval_w.finish();
320
321 let mut digest_input =
325 Vec::with_capacity((signed_region.len() - app_params_offset) + sigval_bytes.len());
326 digest_input.extend_from_slice(&signed_region[app_params_offset..]);
327 digest_input.extend_from_slice(&sigval_bytes);
328 let actual_digest = ring::digest::digest(&ring::digest::SHA256, &digest_input);
329
330 signed_region[digest_value_offset..digest_value_offset + 32]
332 .copy_from_slice(actual_digest.as_ref());
333
334 let inner_len = signed_region.len() + sigval_bytes.len();
335 let mut outer = TlvWriter::with_capacity(inner_len + 10);
336 outer.write_varu64(tlv_type::INTEREST);
337 outer.write_varu64(inner_len as u64);
338 outer.write_raw(&signed_region);
339 outer.write_raw(&sigval_bytes);
340 outer.finish()
341 }
342
343 fn build_signed_interest_region(
359 self,
360 sig_type: SignatureType,
361 key_locator: Option<&Name>,
362 ) -> (Vec<u8>, usize, usize) {
363 let params = self.app_parameters.unwrap_or_default();
364 let lifetime_ms = self.lifetime.map(|d| d.as_millis() as u64).unwrap_or(4000);
365
366 let mut inner = TlvWriter::new();
367
368 inner.write_nested(tlv_type::NAME, |w| {
372 for comp in self.name.components() {
373 w.write_tlv(comp.typ, &comp.value);
374 }
375 w.write_tlv(tlv_type::PARAMETERS_SHA256, &[0u8; 32]);
376 });
377 let digest_value_offset = inner.len() - 32;
379
380 if self.can_be_prefix {
382 inner.write_tlv(tlv_type::CAN_BE_PREFIX, &[]);
383 }
384 if self.must_be_fresh {
385 inner.write_tlv(tlv_type::MUST_BE_FRESH, &[]);
386 }
387
388 if let Some(ref hints) = self.forwarding_hint {
390 inner.write_nested(tlv_type::FORWARDING_HINT, |w| {
391 for h in hints {
392 write_name(w, h);
393 }
394 });
395 }
396
397 inner.write_tlv(tlv_type::NONCE, &next_nonce().to_be_bytes());
399 write_nni(&mut inner, tlv_type::INTEREST_LIFETIME, lifetime_ms);
400 if let Some(h) = self.hop_limit {
401 inner.write_tlv(tlv_type::HOP_LIMIT, &[h]);
402 }
403
404 let app_params_offset = inner.len();
406
407 inner.write_tlv(tlv_type::APP_PARAMETERS, ¶ms);
409
410 inner.write_nested(tlv_type::INTEREST_SIGNATURE_INFO, |w| {
412 write_nni(w, tlv_type::SIGNATURE_TYPE, sig_type.code());
413 if let Some(kl) = key_locator {
414 w.write_nested(tlv_type::KEY_LOCATOR, |w| {
415 write_name(w, kl);
416 });
417 }
418 let nonce_bytes: [u8; 8] = rand_nonce_bytes();
421 w.write_tlv(tlv_type::SIGNATURE_NONCE, &nonce_bytes);
422 let now_ms = std::time::SystemTime::now()
423 .duration_since(std::time::UNIX_EPOCH)
424 .unwrap_or_default()
425 .as_millis() as u64;
426 let (time_buf, time_len) = nni(now_ms);
427 w.write_tlv(tlv_type::SIGNATURE_TIME, &time_buf[..time_len]);
428 });
429
430 (
431 inner.finish().to_vec(),
432 digest_value_offset,
433 app_params_offset,
434 )
435 }
436}
437
438impl From<&str> for Name {
440 fn from(s: &str) -> Self {
441 s.parse().unwrap_or_else(|_| Name::root())
442 }
443}
444
445impl From<String> for Name {
446 fn from(s: String) -> Self {
447 s.parse().unwrap_or_else(|_| Name::root())
448 }
449}
450
451#[cfg(test)]
454mod tests {
455 use super::super::tests::{assert_bytes_eq, name};
456 use super::*;
457 use crate::Interest;
458 use bytes::Bytes;
459 use std::time::Duration;
460
461 #[test]
462 fn interest_roundtrip_name() {
463 let n = name(&[b"localhost", b"ndn-ctl", b"get-stats"]);
464 let bytes = encode_interest(&n, None);
465 let interest = Interest::decode(bytes).unwrap();
466 assert_eq!(*interest.name, n);
467 }
468
469 #[test]
470 fn interest_with_app_params_roundtrip() {
471 let n = name(&[b"localhost", b"ndn-ctl", b"add-route"]);
472 let params = br#"{"cmd":"add_route","prefix":"/ndn","face":1,"cost":10}"#;
473 let bytes = encode_interest(&n, Some(params));
474 let interest = Interest::decode(bytes).unwrap();
475 assert_eq!(interest.name.len(), n.len() + 1);
477 for (i, comp) in n.components().iter().enumerate() {
478 assert_eq!(interest.name.components()[i], *comp);
479 }
480 let last = &interest.name.components()[n.len()];
482 assert_eq!(last.typ, tlv_type::PARAMETERS_SHA256);
483 assert_eq!(last.value.len(), 32);
484 assert_eq!(
485 interest.app_parameters().map(|b| b.as_ref()),
486 Some(params.as_ref())
487 );
488 }
489
490 #[test]
491 fn interest_has_nonce_and_lifetime() {
492 let n = name(&[b"test"]);
493 let bytes = encode_interest(&n, None);
494 let interest = Interest::decode(bytes).unwrap();
495 assert!(interest.nonce().is_some());
496 assert_eq!(interest.lifetime(), Some(Duration::from_millis(4000)));
497 }
498
499 #[test]
500 fn ensure_nonce_adds_when_missing() {
501 let n = name(&[b"test"]);
502 let mut w = TlvWriter::new();
503 w.write_nested(tlv_type::INTEREST, |w| {
504 super::write_name(w, &n);
505 w.write_tlv(tlv_type::INTEREST_LIFETIME, &4000u64.to_be_bytes());
506 });
507 let no_nonce = w.finish();
508 let interest = Interest::decode(no_nonce.clone()).unwrap();
509 assert!(interest.nonce().is_none());
510
511 let with_nonce = ensure_nonce(&no_nonce);
512 let interest2 = Interest::decode(with_nonce).unwrap();
513 assert!(interest2.nonce().is_some());
514 }
515
516 #[test]
517 fn ensure_nonce_preserves_existing() {
518 let n = name(&[b"test"]);
519 let bytes = encode_interest(&n, None);
520 let original_nonce = Interest::decode(bytes.clone()).unwrap().nonce();
521 let result = ensure_nonce(&bytes);
522 assert_eq!(result, bytes); let after = Interest::decode(result).unwrap().nonce();
524 assert_eq!(original_nonce, after);
525 }
526
527 #[test]
528 fn nonces_are_unique() {
529 let n = name(&[b"test"]);
530 let b1 = encode_interest(&n, None);
531 let b2 = encode_interest(&n, None);
532 let i1 = Interest::decode(b1).unwrap();
533 let i2 = Interest::decode(b2).unwrap();
534 assert_ne!(i1.nonce(), i2.nonce());
535 }
536
537 #[test]
540 fn interest_builder_basic() {
541 let wire = InterestBuilder::new("/ndn/test").build();
542 let interest = Interest::decode(wire).unwrap();
543 assert_eq!(interest.name.to_string(), "/ndn/test");
544 assert!(interest.nonce().is_some());
545 assert_eq!(interest.lifetime(), Some(Duration::from_millis(4000)));
546 }
547
548 #[test]
549 fn interest_builder_custom_lifetime() {
550 let wire = InterestBuilder::new("/test")
551 .lifetime(Duration::from_millis(2000))
552 .build();
553 let interest = Interest::decode(wire).unwrap();
554 assert_eq!(interest.lifetime(), Some(Duration::from_millis(2000)));
555 }
556
557 #[test]
558 fn interest_builder_from_str() {
559 let wire = InterestBuilder::new("/a/b/c").build();
560 let interest = Interest::decode(wire).unwrap();
561 assert_eq!(interest.name.len(), 3);
562 }
563
564 #[test]
565 fn interest_builder_app_params_preserves_selectors() {
566 let wire = InterestBuilder::new("/cmd")
567 .can_be_prefix()
568 .must_be_fresh()
569 .lifetime(Duration::from_millis(2000))
570 .hop_limit(64)
571 .app_parameters(b"payload".to_vec())
572 .build();
573 let interest = Interest::decode(wire).unwrap();
574 assert!(interest.selectors().can_be_prefix);
575 assert!(interest.selectors().must_be_fresh);
576 assert_eq!(interest.lifetime(), Some(Duration::from_millis(2000)));
577 assert_eq!(interest.hop_limit(), Some(64));
578 assert_eq!(
579 interest.app_parameters().map(|b| b.as_ref()),
580 Some(b"payload".as_ref())
581 );
582 }
583
584 #[test]
585 fn interest_builder_forwarding_hint() {
586 let hint: Name = "/ndn/gateway".parse().unwrap();
587 let wire = InterestBuilder::new("/test")
588 .forwarding_hint(vec![hint])
589 .build();
590 let interest = Interest::decode(wire).unwrap();
591 let hints = interest.forwarding_hint().expect("forwarding_hint present");
592 assert_eq!(hints.len(), 1);
593 assert_eq!(hints[0].to_string(), "/ndn/gateway");
594 }
595
596 #[test]
597 fn interest_builder_sign_sync_roundtrip() {
598 let key_name: Name = "/key/test".parse().unwrap();
599 let wire = InterestBuilder::new("/signed/cmd")
600 .app_parameters(b"params".to_vec())
601 .sign_sync(
602 crate::SignatureType::SignatureEd25519,
603 Some(&key_name),
604 |region| {
605 let digest = ring::digest::digest(&ring::digest::SHA256, region);
606 Bytes::copy_from_slice(digest.as_ref())
607 },
608 );
609 let interest = Interest::decode(wire).unwrap();
610 assert_eq!(interest.name.components()[0].value.as_ref(), b"signed");
611 assert_eq!(interest.name.components()[1].value.as_ref(), b"cmd");
612 let last = interest.name.components().last().unwrap();
613 assert_eq!(last.typ, tlv_type::PARAMETERS_SHA256);
614 assert_eq!(last.value.len(), 32);
615 let si = interest.sig_info().expect("sig_info present");
616 assert_eq!(si.sig_type, crate::SignatureType::SignatureEd25519);
617 let kl = si.key_locator.as_ref().expect("key locator present");
618 assert_eq!(kl.to_string(), "/key/test");
619 assert!(interest.sig_value().is_some());
620 assert_eq!(
621 interest.app_parameters().map(|b| b.as_ref()),
622 Some(b"params".as_ref())
623 );
624 }
625
626 #[test]
627 fn interest_builder_sign_sync_auto_anti_replay() {
628 let wire = InterestBuilder::new("/cmd").sign_sync(
629 crate::SignatureType::SignatureEd25519,
630 None,
631 |region| {
632 Bytes::copy_from_slice(ring::digest::digest(&ring::digest::SHA256, region).as_ref())
633 },
634 );
635 let interest = Interest::decode(wire).unwrap();
636 let si = interest.sig_info().expect("sig_info");
637 assert!(si.sig_nonce.is_some());
638 assert!(si.sig_time.is_some());
639 }
640
641 #[test]
642 fn interest_builder_sign_sync_empty_params_default() {
643 let wire = InterestBuilder::new("/cmd").sign_sync(
644 crate::SignatureType::DigestSha256,
645 None,
646 |region| {
647 let d = ring::digest::digest(&ring::digest::SHA256, region);
648 Bytes::copy_from_slice(d.as_ref())
649 },
650 );
651 let interest = Interest::decode(wire).unwrap();
652 let ap = interest.app_parameters().expect("app_params present");
653 assert!(ap.is_empty());
654 }
655
656 #[test]
657 fn interest_builder_sign_sync_signed_region() {
658 let wire = InterestBuilder::new("/test")
659 .app_parameters(b"data".to_vec())
660 .sign_sync(crate::SignatureType::SignatureEd25519, None, |region| {
661 assert_eq!(region[0], tlv_type::NAME as u8);
662 Bytes::copy_from_slice(ring::digest::digest(&ring::digest::SHA256, region).as_ref())
663 });
664 let interest = Interest::decode(wire.clone()).unwrap();
665 let region = interest.signed_region().expect("signed region present");
666 assert_eq!(region[0], tlv_type::NAME as u8);
667 assert!(interest.sig_value().is_some());
668 }
669
670 #[test]
671 fn interest_builder_sign_async_matches_sync_structure() {
672 use std::pin::pin;
673 use std::task::{Context, Wake, Waker};
674
675 struct NoopWaker;
676 impl Wake for NoopWaker {
677 fn wake(self: std::sync::Arc<Self>) {}
678 }
679 let waker = Waker::from(std::sync::Arc::new(NoopWaker));
680 let mut cx = Context::from_waker(&waker);
681
682 let fut = InterestBuilder::new("/test")
683 .app_parameters(b"p".to_vec())
684 .sign(
685 crate::SignatureType::SignatureEd25519,
686 None,
687 |region: &[u8]| {
688 let d = ring::digest::digest(&ring::digest::SHA256, region);
689 std::future::ready(Bytes::copy_from_slice(d.as_ref()))
690 },
691 );
692 let mut fut = pin!(fut);
693 let async_wire = match fut.as_mut().poll(&mut cx) {
694 std::task::Poll::Ready(b) => b,
695 std::task::Poll::Pending => panic!("should complete immediately"),
696 };
697
698 let async_i = Interest::decode(async_wire).unwrap();
699 assert!(async_i.sig_info().is_some());
700 assert!(async_i.sig_value().is_some());
701 assert!(async_i.signed_region().is_some());
702
703 let sync_wire = InterestBuilder::new("/test")
704 .app_parameters(b"p".to_vec())
705 .sign_sync(crate::SignatureType::SignatureEd25519, None, |region| {
706 Bytes::copy_from_slice(ring::digest::digest(&ring::digest::SHA256, region).as_ref())
707 });
708 let sync_i = Interest::decode(sync_wire).unwrap();
709 assert!(sync_i.sig_info().is_some());
710 assert!(sync_i.sig_value().is_some());
711 }
712
713 #[test]
714 fn interest_builder_sign_sync_with_all_options() {
715 let hint: Name = "/ndn/relay".parse().unwrap();
716 let key_name: Name = "/my/key".parse().unwrap();
717 let wire = InterestBuilder::new("/prefix/command")
718 .can_be_prefix()
719 .must_be_fresh()
720 .lifetime(Duration::from_millis(8000))
721 .hop_limit(32)
722 .forwarding_hint(vec![hint])
723 .app_parameters(b"payload".to_vec())
724 .sign_sync(
725 crate::SignatureType::SignatureEd25519,
726 Some(&key_name),
727 |region| {
728 Bytes::copy_from_slice(
729 ring::digest::digest(&ring::digest::SHA256, region).as_ref(),
730 )
731 },
732 );
733 let i = Interest::decode(wire).unwrap();
734 assert!(i.selectors().can_be_prefix);
735 assert!(i.selectors().must_be_fresh);
736 assert_eq!(i.lifetime(), Some(Duration::from_millis(8000)));
737 assert_eq!(i.hop_limit(), Some(32));
738 let hints = i.forwarding_hint().expect("forwarding_hint");
739 assert_eq!(hints[0].to_string(), "/ndn/relay");
740 assert_eq!(
741 i.app_parameters().map(|b| b.as_ref()),
742 Some(b"payload".as_ref())
743 );
744 let si = i.sig_info().expect("sig_info");
745 assert_eq!(si.sig_type, crate::SignatureType::SignatureEd25519);
746 assert_eq!(si.key_locator.as_ref().unwrap().to_string(), "/my/key");
747 assert!(i.sig_value().is_some());
748 assert!(i.signed_region().is_some());
749 }
750
751 #[test]
754 fn wire_interest_nni_lifetime() {
755 let wire = encode_interest(&name(&[b"ndn", b"edu"]), None);
756
757 let pos = wire
758 .windows(2)
759 .position(|w| w == [0x0C, 0x02])
760 .expect("InterestLifetime should be type=0x0C len=0x02 (2 bytes)");
761 assert_bytes_eq(
762 &wire[pos..pos + 4],
763 &[0x0C, 0x02, 0x0F, 0xA0],
764 "InterestLifetime 4000ms",
765 );
766 }
767
768 #[test]
769 fn wire_interest_structure() {
770 let wire = encode_interest(&name(&[b"A"]), None);
771
772 assert_eq!(wire[0], 0x05, "outer type must be Interest (0x05)");
773
774 let name_expected = [0x07, 0x03, 0x08, 0x01, 0x41];
775 assert_bytes_eq(&wire[2..7], &name_expected, "Name /A");
776
777 assert_eq!(wire[7], 0x0A, "Nonce type");
778 assert_eq!(wire[8], 0x04, "Nonce length");
779
780 assert_bytes_eq(&wire[13..17], &[0x0C, 0x02, 0x0F, 0xA0], "Lifetime");
781
782 assert_eq!(wire.len(), 17, "total Interest length");
783 }
784
785 #[test]
786 fn wire_interest_builder_selectors() {
787 let wire = InterestBuilder::new("/A")
788 .can_be_prefix()
789 .must_be_fresh()
790 .lifetime(Duration::from_millis(1000))
791 .build();
792
793 let after_name = 7;
794 assert_bytes_eq(
795 &wire[after_name..after_name + 2],
796 &[0x21, 0x00],
797 "CanBePrefix",
798 );
799 assert_bytes_eq(
800 &wire[after_name + 2..after_name + 4],
801 &[0x12, 0x00],
802 "MustBeFresh",
803 );
804 assert_eq!(wire[after_name + 4], 0x0A, "Nonce type");
805 let lt_pos = after_name + 4 + 6;
806 assert_bytes_eq(
807 &wire[lt_pos..lt_pos + 4],
808 &[0x0C, 0x02, 0x03, 0xE8],
809 "Lifetime 1000ms",
810 );
811 }
812
813 #[test]
814 fn wire_ndnd_interest_decode() {
815 let ndnd_wire: &[u8] = &[
816 0x05, 0x16, 0x07, 0x0A, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x03, 0x65, 0x64, 0x75,
817 0x0A, 0x04, 0x01, 0x02, 0x03, 0x04, 0x0C, 0x02, 0x0F, 0xA0,
818 ];
819 let interest = Interest::decode(Bytes::from_static(ndnd_wire)).unwrap();
820 assert_eq!(interest.name.to_string(), "/ndn/edu");
821 assert_eq!(interest.nonce(), Some(0x01020304));
822 assert_eq!(interest.lifetime(), Some(Duration::from_millis(4000)));
823 }
824
825 #[test]
826 fn wire_ndnd_interest_1byte_lifetime_decode() {
827 let ndnd_wire: &[u8] = &[
828 0x05, 0x15, 0x07, 0x0A, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x03, 0x65, 0x64, 0x75,
829 0x0A, 0x04, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x01, 0x64,
830 ];
831 let interest = Interest::decode(Bytes::from_static(ndnd_wire)).unwrap();
832 assert_eq!(interest.lifetime(), Some(Duration::from_millis(100)));
833 }
834
835 #[test]
836 fn wire_ndnd_interest_4byte_lifetime_decode() {
837 let ndnd_wire: &[u8] = &[
838 0x05, 0x18, 0x07, 0x0A, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x03, 0x65, 0x64, 0x75,
839 0x0A, 0x04, 0x00, 0x00, 0x00, 0x01, 0x0C, 0x04, 0x00, 0x01, 0x86, 0xA0,
840 ];
841 let interest = Interest::decode(Bytes::from_static(ndnd_wire)).unwrap();
842 assert_eq!(interest.lifetime(), Some(Duration::from_millis(100000)));
843 }
844}