1use crate::{Keypair, PublicKey};
4use bytes::{Bytes, BytesMut};
5use ed25519_dalek::{Signature, SignatureError};
6use self_cell::self_cell;
7use simple_dns::{
8 rdata::{RData, A, AAAA, HTTPS, SVCB, TXT},
9 Name, Packet, ResourceRecord, SimpleDnsError, CLASS,
10};
11use std::{
12 char,
13 fmt::{self, Debug, Display, Formatter},
14 net::{IpAddr, Ipv4Addr, Ipv6Addr},
15};
16
17use serde::{Deserialize, Serialize};
18
19use ntimestamp::Timestamp;
20
21#[derive(Debug, Clone, Default)]
22pub struct SignedPacketBuilder {
25 records: Vec<ResourceRecord<'static>>,
26 timestamp: Option<Timestamp>,
27}
28
29impl SignedPacketBuilder {
30 pub fn record(mut self, record: ResourceRecord<'_>) -> Self {
32 self.records.push(record.into_owned());
33 self
34 }
35
36 pub fn rdata(self, name: Name<'_>, rdata: RData, ttl: u32) -> Self {
41 self.record(ResourceRecord::new(name.to_owned(), CLASS::IN, ttl, rdata))
42 }
43
44 pub fn a(self, name: Name<'_>, address: Ipv4Addr, ttl: u32) -> Self {
49 self.rdata(
50 name,
51 RData::A(A {
52 address: address.into(),
53 }),
54 ttl,
55 )
56 }
57
58 pub fn aaaa(self, name: Name<'_>, address: Ipv6Addr, ttl: u32) -> Self {
63 self.rdata(
64 name,
65 RData::AAAA(AAAA {
66 address: address.into(),
67 }),
68 ttl,
69 )
70 }
71
72 pub fn address(self, name: Name<'_>, address: IpAddr, ttl: u32) -> Self {
77 match address {
78 IpAddr::V4(addr) => self.a(name, addr, ttl),
79 IpAddr::V6(addr) => self.aaaa(name, addr, ttl),
80 }
81 }
82
83 pub fn cname(self, name: Name<'_>, cname: Name<'_>, ttl: u32) -> Self {
88 self.rdata(name, RData::CNAME(cname.into()), ttl)
89 }
90
91 pub fn txt(self, name: Name<'_>, text: TXT<'_>, ttl: u32) -> Self {
96 self.rdata(name, RData::TXT(text), ttl)
97 }
98
99 pub fn https(self, name: Name<'_>, svcb: SVCB, ttl: u32) -> Self {
104 self.rdata(name, RData::HTTPS(HTTPS(svcb)), ttl)
105 }
106
107 pub fn svcb(self, name: Name<'_>, svcb: SVCB, ttl: u32) -> Self {
112 self.rdata(name, RData::SVCB(svcb), ttl)
113 }
114
115 pub fn timestamp(mut self, timestamp: Timestamp) -> Self {
117 self.timestamp = Some(timestamp);
118
119 self
120 }
121
122 pub fn build(self, keypair: &Keypair) -> Result<SignedPacket, SignedPacketBuildError> {
124 self.sign(keypair)
125 }
126
127 pub fn sign(self, keypair: &Keypair) -> Result<SignedPacket, SignedPacketBuildError> {
132 SignedPacket::new(
133 keypair,
134 &self.records,
135 self.timestamp.unwrap_or(Timestamp::now()),
136 )
137 }
138}
139
140const DOT: char = '.';
141
142self_cell!(
143 struct Inner {
144 owner: Bytes,
145
146 #[covariant]
147 dependent: Packet,
148 }
149
150 impl{PartialEq, Eq}
151);
152
153impl Inner {
154 fn try_from_parts(
155 public_key: &PublicKey,
156 signature: &Signature,
157 timestamp: u64,
158 encoded_packet: &[u8],
159 ) -> Result<Self, SimpleDnsError> {
160 let mut bytes = BytesMut::with_capacity(encoded_packet.len() + 104);
162
163 bytes.extend_from_slice(public_key.as_bytes());
164 bytes.extend_from_slice(&signature.to_bytes());
165 bytes.extend_from_slice(×tamp.to_be_bytes());
166 bytes.extend_from_slice(encoded_packet);
167
168 Self::try_new(bytes.into(), |bytes| Packet::parse(&bytes[104..]))
169 }
170
171 fn try_from_bytes(bytes: Bytes) -> Result<Self, SimpleDnsError> {
172 Inner::try_new(bytes, |bytes| Packet::parse(&bytes[104..]))
173 }
174}
175
176#[derive(PartialEq, Eq)]
177pub struct SignedPacket {
179 inner: Inner,
180 last_seen: Timestamp,
181}
182
183impl SignedPacket {
184 pub const MAX_BYTES: u64 = 1104;
189
190 pub fn builder() -> SignedPacketBuilder {
239 SignedPacketBuilder::default()
240 }
241
242 pub fn new(
250 keypair: &Keypair,
251 answers: &[ResourceRecord<'_>],
252 timestamp: Timestamp,
253 ) -> Result<SignedPacket, SignedPacketBuildError> {
254 let mut packet = Packet::new_reply(0);
255
256 let origin = keypair.public_key().to_z32();
257
258 let normalized_names: Vec<String> = answers
260 .iter()
261 .map(|answer| normalize_name(&origin, answer.name.to_string()))
262 .collect();
263
264 answers.iter().enumerate().for_each(|(index, answer)| {
265 packet.answers.push(ResourceRecord::new(
266 Name::new_unchecked(&normalized_names[index]).to_owned(),
267 answer.class,
268 answer.ttl,
269 answer.rdata.clone(),
270 ))
271 });
272
273 let encoded_packet = packet.build_bytes_vec_compressed()?;
275
276 if encoded_packet.len() > 1000 {
277 return Err(SignedPacketBuildError::PacketTooLarge(encoded_packet.len()));
278 }
279
280 let signature = keypair.sign(&signable(timestamp.into(), &encoded_packet));
281
282 Ok(SignedPacket {
283 inner: Inner::try_from_parts(
284 &keypair.public_key(),
285 &signature,
286 timestamp.into(),
287 &encoded_packet,
288 )
289 .expect("SignedPacket::new() try_from_parts should not fail"),
290 last_seen: Timestamp::now(),
291 })
292 }
293
294 pub fn from_relay_payload(
296 public_key: &PublicKey,
297 payload: &Bytes,
298 ) -> Result<SignedPacket, SignedPacketVerifyError> {
299 let mut bytes = BytesMut::with_capacity(payload.len() + 32);
300
301 bytes.extend_from_slice(public_key.as_bytes());
302 bytes.extend_from_slice(payload);
303
304 SignedPacket::from_bytes(&bytes.into())
305 }
306
307 pub fn as_bytes(&self) -> &Bytes {
312 self.inner.borrow_owner()
313 }
314
315 pub fn serialize(&self) -> Vec<u8> {
318 let mut bytes = Vec::with_capacity(SignedPacket::MAX_BYTES as usize);
319 bytes.extend_from_slice(&self.last_seen.to_bytes());
320 bytes.extend_from_slice(self.as_bytes());
321
322 bytes
323 }
324
325 pub fn deserialize(bytes: &[u8]) -> Result<Self, SimpleDnsError> {
334 let mut last_seen = Timestamp::try_from(&bytes[0..8]).unwrap_or_default();
335
336 if last_seen > (Timestamp::now() + 60_000_000) {
337 last_seen = Timestamp::from(0)
338 }
339
340 Ok(SignedPacket {
341 inner: Inner::try_from_bytes(bytes[8..].to_owned().into())?,
342 last_seen,
343 })
344 }
345
346 pub fn to_relay_payload(&self) -> Bytes {
349 self.inner.borrow_owner().slice(32..)
350 }
351
352 pub fn public_key(&self) -> PublicKey {
354 PublicKey::try_from(&self.inner.borrow_owner()[0..32]).expect("SignedPacket::public_key()")
355 }
356
357 pub fn signature(&self) -> Signature {
360 Signature::try_from(&self.inner.borrow_owner()[32..96]).expect("SignedPacket::signature()")
361 }
362
363 pub fn timestamp(&self) -> Timestamp {
370 let bytes = self.inner.borrow_owner();
371 let slice: [u8; 8] = bytes[96..104]
372 .try_into()
373 .expect("SignedPacket::timestamp()");
374
375 u64::from_be_bytes(slice).into()
376 }
377
378 pub fn encoded_packet(&self) -> Bytes {
380 self.inner.borrow_owner().slice(104..)
381 }
382
383 pub(crate) fn packet(&self) -> &Packet<'_> {
385 self.inner.borrow_dependent()
386 }
387
388 pub fn last_seen(&self) -> &Timestamp {
390 &self.last_seen
391 }
392
393 pub fn set_last_seen(&mut self, last_seen: &Timestamp) {
397 self.last_seen = last_seen.into();
398 }
399
400 pub fn refresh(&mut self) {
404 self.last_seen = Timestamp::now();
405 }
406
407 pub fn more_recent_than(&self, other: &SignedPacket) -> bool {
413 if self.timestamp() == other.timestamp() {
416 self.encoded_packet() > other.encoded_packet()
417 } else {
418 self.timestamp() > other.timestamp()
419 }
420 }
421
422 pub fn is_same_as(&self, other: &SignedPacket) -> bool {
425 self.as_bytes() == other.as_bytes()
426 }
427
428 pub fn resource_records(&self, name: &str) -> impl Iterator<Item = &ResourceRecord<'_>> {
435 let origin = self.public_key().to_z32();
436 let normalized_name = normalize_name(&origin, name.to_string());
437 let is_wildcard = normalized_name.starts_with('*');
438
439 self.all_resource_records().filter(move |rr| {
440 if is_wildcard {
441 rr.name
442 .to_string()
443 .strip_suffix(&normalized_name[1..])
444 .map(|m| !m.contains('.'))
445 .unwrap_or_default()
446 } else {
447 rr.name.to_string() == normalized_name
448 }
449 })
450 }
451
452 pub fn fresh_resource_records(&self, name: &str) -> impl Iterator<Item = &ResourceRecord<'_>> {
455 self.resource_records(name)
456 .filter(move |rr| rr.ttl > self.elapsed())
457 }
458
459 pub fn all_resource_records(&self) -> impl Iterator<Item = &ResourceRecord<'_>> {
461 self.packet().answers.iter()
462 }
463
464 pub fn expires_in(&self, min: u32, max: u32) -> u32 {
471 match self.ttl(min, max).overflowing_sub(self.elapsed()) {
472 (_, true) => 0,
473 (ttl, false) => ttl,
474 }
475 }
476
477 pub fn ttl(&self, min: u32, max: u32) -> u32 {
484 self.packet()
485 .answers
486 .iter()
487 .map(|rr| rr.ttl)
488 .min()
489 .map_or(min, |v| v.clamp(min, max))
490 }
491
492 pub fn is_expired(&self, min: u32, max: u32) -> bool {
495 self.expires_in(min, max) == 0
496 }
497
498 pub fn elapsed(&self) -> u32 {
500 ((Timestamp::now().as_u64() - self.last_seen.as_u64()) / 1_000_000) as u32
501 }
502
503 fn from_bytes(bytes: &Bytes) -> Result<SignedPacket, SignedPacketVerifyError> {
518 if bytes.len() < 104 {
519 return Err(SignedPacketVerifyError::InvalidSignedPacketBytesLength(
520 bytes.len(),
521 ));
522 }
523 if (bytes.len() as u64) > SignedPacket::MAX_BYTES {
524 return Err(SignedPacketVerifyError::PacketTooLarge(bytes.len()));
525 }
526 let public_key = PublicKey::try_from(&bytes[..32])?;
527 let signature = Signature::from_bytes(
528 bytes[32..96]
529 .try_into()
530 .expect("SignedPacket::from_bytes(); Signature from 64 bytes"),
531 );
532 let timestamp = u64::from_be_bytes(
533 bytes[96..104]
534 .try_into()
535 .expect("SignedPacket::from_bytes(); Timestamp from 8 bytes"),
536 );
537
538 let encoded_packet = &bytes[104..];
539
540 public_key.verify(&signable(timestamp, encoded_packet), &signature)?;
541
542 Ok(SignedPacket {
543 inner: Inner::try_from_bytes(bytes.to_owned())?,
544 last_seen: Timestamp::now(),
545 })
546 }
547
548 fn from_bytes_unchecked(bytes: &Bytes, last_seen: impl Into<Timestamp>) -> SignedPacket {
551 SignedPacket {
552 inner: Inner::try_from_bytes(bytes.to_owned())
553 .expect("called SignedPacket::from_bytes_unchecked on invalid bytes"),
554 last_seen: last_seen.into(),
555 }
556 }
557}
558
559fn signable(timestamp: u64, v: &[u8]) -> Box<[u8]> {
560 let mut signable = format!("3:seqi{}e1:v{}:", timestamp, v.len()).into_bytes();
561 signable.extend(v);
562 signable.into()
563}
564
565fn normalize_name(origin: &str, name: String) -> String {
566 let name = if name.ends_with(DOT) {
567 name[..name.len() - 1].to_string()
568 } else {
569 name
570 };
571
572 let parts: Vec<&str> = name.split('.').collect();
573 let last = *parts.last().unwrap_or(&"");
574
575 if last == origin {
576 return name.to_string();
578 }
579
580 if last == "@" || last.is_empty() {
581 return origin.to_string();
583 }
584
585 format!("{name}.{origin}")
586}
587
588#[cfg(dht)]
589use crate::mainline::MutableItem;
590
591use super::keys::PublicKeyError;
592
593#[cfg(dht)]
594impl From<&SignedPacket> for MutableItem {
595 fn from(s: &SignedPacket) -> Self {
596 Self::new_signed_unchecked(
597 s.public_key().to_bytes(),
598 s.signature().to_bytes(),
599 s.inner.borrow_owner()[104..].into(),
601 s.timestamp().as_u64() as i64,
602 None,
603 )
604 }
605}
606
607#[cfg(dht)]
608impl TryFrom<&MutableItem> for SignedPacket {
609 type Error = SignedPacketVerifyError;
610
611 fn try_from(i: &MutableItem) -> Result<Self, SignedPacketVerifyError> {
612 let public_key = PublicKey::try_from(i.key())?;
613 let seq = i.seq() as u64;
614 let signature: Signature = i.signature().into();
615
616 Ok(Self {
617 inner: Inner::try_from_parts(&public_key, &signature, seq, i.value())?,
618 last_seen: Timestamp::now(),
619 })
620 }
621}
622
623#[cfg(dht)]
624impl TryFrom<MutableItem> for SignedPacket {
625 type Error = SignedPacketVerifyError;
626
627 fn try_from(i: MutableItem) -> Result<Self, SignedPacketVerifyError> {
628 SignedPacket::try_from(&i)
629 }
630}
631
632impl AsRef<[u8]> for SignedPacket {
633 fn as_ref(&self) -> &[u8] {
636 self.inner.borrow_owner()
637 }
638}
639
640impl Clone for SignedPacket {
641 fn clone(&self) -> Self {
642 Self::from_bytes_unchecked(self.as_bytes(), self.last_seen)
643 }
644}
645
646impl Debug for SignedPacket {
647 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
648 f.debug_struct("SignedPacket")
649 .field("timestamp", &self.timestamp())
650 .field("last_seen", &self.last_seen())
651 .field("public_key", &self.public_key())
652 .field("signature", &self.signature())
653 .field("packet", &self.packet())
654 .finish()
655 }
656}
657
658impl Display for SignedPacket {
659 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
660 write!(
661 f,
662 "SignedPacket ({}):\n last_seen: {} seconds ago\n timestamp: {} {},\n signature: {}\n records:\n",
663 &self.public_key(),
664 &self.elapsed(),
665 &self.timestamp(),
666 &self.timestamp().format_http_date(),
667 &self.signature(),
668 )?;
669
670 for answer in &self.packet().answers {
671 writeln!(
672 f,
673 " {} IN {} {}",
674 &answer.name,
675 &answer.ttl,
676 match &answer.rdata {
677 RData::A(A { address }) => format!("A {}", Ipv4Addr::from(*address)),
678 RData::AAAA(AAAA { address }) => format!("AAAA {}", Ipv6Addr::from(*address)),
679 #[allow(clippy::to_string_in_format_args)]
680 RData::CNAME(name) => format!("CNAME {}", name.to_string()),
681 RData::TXT(txt) => {
682 format!(
683 "TXT \"{}\"",
684 txt.clone()
685 .try_into()
686 .unwrap_or("__INVALID_TXT_VALUE_".to_string())
687 )
688 }
689 _ => format!("{:?}", answer.rdata),
690 }
691 )?;
692 }
693
694 writeln!(f)?;
695
696 Ok(())
697 }
698}
699
700impl Serialize for SignedPacket {
703 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
705 where
706 S: serde::Serializer,
707 {
708 self.serialize().serialize(serializer)
709 }
710}
711
712impl<'de> Deserialize<'de> for SignedPacket {
713 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
715 where
716 D: serde::Deserializer<'de>,
717 {
718 let bytes: Vec<u8> = Deserialize::deserialize(deserializer)?;
719
720 SignedPacket::deserialize(&bytes).map_err(serde::de::Error::custom)
721 }
722}
723
724#[derive(thiserror::Error, Debug)]
725pub enum SignedPacketVerifyError {
727 #[error(transparent)]
728 SignatureError(#[from] SignatureError),
730
731 #[error(transparent)]
732 DnsError(#[from] simple_dns::SimpleDnsError),
734
735 #[error("Invalid SignedPacket bytes length, expected at least 104 bytes but got: {0}")]
736 InvalidSignedPacketBytesLength(usize),
739
740 #[error("DNS Packet is too large, expected max 1000 bytes but got: {0}")]
741 PacketTooLarge(usize),
743
744 #[error(transparent)]
745 PublicKeyError(#[from] PublicKeyError),
747}
748
749#[derive(thiserror::Error, Debug, PartialEq, Eq)]
750pub enum SignedPacketBuildError {
752 #[error("DNS Packet is too large, expected max 1000 bytes but got: {0}")]
753 PacketTooLarge(usize),
755
756 #[error("Failed to write encoded DNS packet due to I/O error: {0}")]
757 FailedToWrite(#[from] SimpleDnsError),
759}
760
761#[cfg(test)]
762mod tests {
763 use simple_dns::rdata::CNAME;
764
765 use super::*;
766
767 use crate::{DEFAULT_MAXIMUM_TTL, DEFAULT_MINIMUM_TTL};
768
769 #[test]
770 fn custom_timestamp() {
771 let timestamp = Timestamp::from(42);
772
773 let signed_packet = SignedPacket::builder()
774 .timestamp(timestamp)
775 .sign(&Keypair::random())
776 .unwrap();
777
778 assert_eq!(signed_packet.timestamp(), timestamp);
779 }
780
781 #[test]
782 fn normalize_names() {
783 let origin = "ed4mn3aoazuf1ahpy9rz1nyswhukbj5483ryefwkue7fbp3egkzo";
784
785 assert_eq!(normalize_name(origin, ".".to_string()), origin);
786 assert_eq!(normalize_name(origin, "@".to_string()), origin);
787 assert_eq!(normalize_name(origin, "@.".to_string()), origin);
788 assert_eq!(normalize_name(origin, origin.to_string()), origin);
789 assert_eq!(
790 normalize_name(origin, "_derp_region.irorh".to_string()),
791 format!("_derp_region.irorh.{origin}")
792 );
793 assert_eq!(
794 normalize_name(origin, format!("_derp_region.irorh.{origin}")),
795 format!("_derp_region.irorh.{origin}")
796 );
797 assert_eq!(
798 normalize_name(origin, format!("_derp_region.irorh.{origin}.")),
799 format!("_derp_region.irorh.{origin}")
800 );
801 }
802
803 #[test]
804 fn sign_verify() {
805 let keypair = Keypair::random();
806
807 let signed_packet = SignedPacket::builder()
808 .address(
809 "_derp_region.iroh.".try_into().unwrap(),
810 "1.1.1.1".parse().unwrap(),
811 30,
812 )
813 .sign(&keypair)
814 .unwrap();
815
816 assert!(SignedPacket::from_relay_payload(
817 &signed_packet.public_key(),
818 &signed_packet.to_relay_payload()
819 )
820 .is_ok());
821 }
822
823 #[test]
824 fn from_too_large_bytes() {
825 let keypair = Keypair::random();
826
827 let bytes = vec![0; 1073];
828 let error = SignedPacket::from_relay_payload(&keypair.public_key(), &bytes.into());
829
830 assert!(error.is_err());
831 }
832
833 #[test]
834 fn from_too_large_packet() {
835 let keypair = Keypair::random();
836
837 let mut builder = SignedPacket::builder();
838
839 for _ in 0..100 {
840 builder = builder.address(
841 "_derp_region.iroh.".try_into().unwrap(),
842 "1.1.1.1".parse().unwrap(),
843 30,
844 );
845 }
846
847 let error = builder.sign(&keypair);
848
849 assert!(error.is_err());
850 }
851
852 #[test]
853 fn resource_records_iterator() {
854 let keypair = Keypair::random();
855
856 let target = ResourceRecord::new(
857 Name::new("_derp_region.iroh.").unwrap(),
858 simple_dns::CLASS::IN,
859 30,
860 RData::A(A {
861 address: Ipv4Addr::new(1, 1, 1, 1).into(),
862 }),
863 );
864
865 let signed_packet = SignedPacket::builder()
866 .record(target.clone())
867 .address(
868 "something-else".try_into().unwrap(),
869 "1.1.1.1".parse().unwrap(),
870 30,
871 )
872 .sign(&keypair)
873 .unwrap();
874
875 let iter = signed_packet.resource_records("_derp_region.iroh");
876 assert_eq!(iter.count(), 1);
877
878 for record in signed_packet.resource_records("_derp_region.iroh") {
879 assert_eq!(record.rdata, target.rdata);
880 }
881 }
882
883 #[cfg(dht)]
884 #[test]
885 fn to_mutable() {
886 let keypair = Keypair::random();
887
888 let signed_packet = SignedPacket::builder()
889 .address(
890 "_derp_region.iroh.".try_into().unwrap(),
891 "1.1.1.1".parse().unwrap(),
892 30,
893 )
894 .sign(&keypair)
895 .unwrap();
896
897 let item: MutableItem = (&signed_packet).into();
898 let seq = signed_packet.timestamp().as_u64() as i64;
899
900 let expected = MutableItem::new(
901 &keypair.secret_key().into(),
902 &signed_packet.packet().build_bytes_vec_compressed().unwrap(),
903 seq,
904 None,
905 );
906
907 assert_eq!(item, expected);
908 }
909
910 #[test]
911 fn compressed_names() {
912 let keypair = Keypair::random();
913
914 let signed_packet = SignedPacket::builder()
915 .cname(".".try_into().unwrap(), "foobar".try_into().unwrap(), 30)
916 .cname(".".try_into().unwrap(), "foobar".try_into().unwrap(), 30)
917 .sign(&keypair)
918 .unwrap();
919
920 assert_eq!(
921 signed_packet
922 .resource_records("@")
923 .map(|r| r.rdata.clone())
924 .collect::<Vec<_>>(),
925 vec![
926 RData::CNAME(CNAME("foobar".try_into().unwrap())),
927 RData::CNAME(CNAME("foobar".try_into().unwrap()))
928 ]
929 )
930 }
931
932 #[test]
933 fn to_bytes_from_bytes() {
934 let keypair = Keypair::random();
935
936 let signed_packet = SignedPacket::builder()
937 .txt("_foo".try_into().unwrap(), "hello".try_into().unwrap(), 30)
938 .sign(&keypair)
939 .unwrap();
940
941 let bytes = signed_packet.as_bytes();
942 let from_bytes = SignedPacket::from_bytes(bytes).unwrap();
943 assert_eq!(signed_packet.as_bytes(), from_bytes.as_bytes());
944 let from_bytes2 = SignedPacket::from_bytes_unchecked(bytes, signed_packet.last_seen);
945 assert_eq!(signed_packet.as_bytes(), from_bytes2.as_bytes());
946
947 let public_key = keypair.public_key();
948 let payload = signed_packet.to_relay_payload();
949 let from_relay_payload = SignedPacket::from_relay_payload(&public_key, &payload).unwrap();
950 assert_eq!(signed_packet.as_bytes(), from_relay_payload.as_bytes());
951 }
952
953 #[test]
954 fn clone() {
955 let keypair = Keypair::random();
956
957 let signed = SignedPacket::builder()
958 .txt("_foo".try_into().unwrap(), "hello".try_into().unwrap(), 30)
959 .sign(&keypair)
960 .unwrap();
961
962 let cloned = signed.clone();
963
964 assert_eq!(cloned.as_bytes(), signed.as_bytes());
965 }
966
967 #[test]
968 fn expires_in_minimum_ttl() {
969 let keypair = Keypair::random();
970
971 let mut signed_packet = SignedPacket::builder()
972 .txt("_foo".try_into().unwrap(), "hello".try_into().unwrap(), 10)
973 .sign(&keypair)
974 .unwrap();
975
976 signed_packet.last_seen -= 20 * 1_000_000_u64;
977
978 assert!(
979 signed_packet.expires_in(30, u32::MAX) > 0,
980 "input minimum_ttl is 30 so ttl = 30"
981 );
982
983 assert!(
984 signed_packet.expires_in(0, u32::MAX) == 0,
985 "input minimum_ttl is 0 so ttl = 10 (smallest in resource records)"
986 );
987 }
988
989 #[test]
990 fn expires_in_maximum_ttl() {
991 let keypair = Keypair::random();
992
993 let mut signed_packet = SignedPacket::builder()
994 .txt(
995 "_foo".try_into().unwrap(),
996 "hello".try_into().unwrap(),
997 3 * DEFAULT_MAXIMUM_TTL,
998 )
999 .sign(&keypair)
1000 .unwrap();
1001
1002 signed_packet.last_seen -= 2 * (DEFAULT_MAXIMUM_TTL as u64) * 1_000_000;
1003
1004 assert!(
1005 signed_packet.expires_in(0, DEFAULT_MAXIMUM_TTL) == 0,
1006 "input maximum_ttl is the dfeault 86400 so maximum ttl = 86400"
1007 );
1008
1009 assert!(
1010 signed_packet.expires_in(0, 7 * DEFAULT_MAXIMUM_TTL) > 0,
1011 "input maximum_ttl is 7 * 86400 so ttl = 3 * 86400 (smallest in resource records)"
1012 );
1013 }
1014
1015 #[test]
1016 fn fresh_resource_records() {
1017 let keypair = Keypair::random();
1018
1019 let mut signed_packet = SignedPacket::builder()
1020 .txt("_foo".try_into().unwrap(), "hello".try_into().unwrap(), 30)
1021 .txt("_foo".try_into().unwrap(), "world".try_into().unwrap(), 60)
1022 .txt("_bar".try_into().unwrap(), "world".try_into().unwrap(), 60)
1023 .sign(&keypair)
1024 .unwrap();
1025
1026 signed_packet.last_seen -= 30 * 1_000_000;
1027
1028 assert_eq!(signed_packet.fresh_resource_records("_foo").count(), 1);
1029 }
1030
1031 #[test]
1032 fn ttl_empty() {
1033 let keypair = Keypair::random();
1034
1035 let signed_packet = SignedPacket::builder().sign(&keypair).unwrap();
1036
1037 assert_eq!(
1038 signed_packet.ttl(DEFAULT_MINIMUM_TTL, DEFAULT_MAXIMUM_TTL),
1039 300
1040 );
1041 }
1042
1043 #[test]
1044 fn ttl_with_records_less_than_minimum() {
1045 let keypair = Keypair::random();
1046
1047 let signed_packet = SignedPacket::builder()
1048 .txt(
1049 "_foo".try_into().unwrap(),
1050 "hello".try_into().unwrap(),
1051 DEFAULT_MINIMUM_TTL / 2,
1052 )
1053 .txt(
1054 "_foo".try_into().unwrap(),
1055 "world".try_into().unwrap(),
1056 DEFAULT_MINIMUM_TTL / 4,
1057 )
1058 .sign(&keypair)
1059 .unwrap();
1060
1061 assert_eq!(
1062 signed_packet.ttl(DEFAULT_MINIMUM_TTL, DEFAULT_MAXIMUM_TTL),
1063 DEFAULT_MINIMUM_TTL
1064 );
1065
1066 assert_eq!(
1067 signed_packet.ttl(0, DEFAULT_MAXIMUM_TTL),
1068 DEFAULT_MINIMUM_TTL / 4
1069 );
1070 }
1071
1072 #[test]
1073 fn ttl_with_records_more_than_maximum() {
1074 let keypair = Keypair::random();
1075
1076 let signed_packet = SignedPacket::builder()
1077 .txt(
1078 "_foo".try_into().unwrap(),
1079 "hello".try_into().unwrap(),
1080 DEFAULT_MAXIMUM_TTL * 2,
1081 )
1082 .txt(
1083 "_foo".try_into().unwrap(),
1084 "world".try_into().unwrap(),
1085 DEFAULT_MAXIMUM_TTL * 4,
1086 )
1087 .sign(&keypair)
1088 .unwrap();
1089
1090 assert_eq!(
1091 signed_packet.ttl(DEFAULT_MINIMUM_TTL, DEFAULT_MAXIMUM_TTL),
1092 DEFAULT_MAXIMUM_TTL
1093 );
1094
1095 assert_eq!(
1096 signed_packet.ttl(0, DEFAULT_MAXIMUM_TTL * 8),
1097 DEFAULT_MAXIMUM_TTL * 2
1098 );
1099 }
1100
1101 #[test]
1102 fn serde() {
1103 use postcard::{from_bytes, to_allocvec};
1104
1105 let keypair = Keypair::random();
1106
1107 let signed_packet = SignedPacket::builder()
1108 .address(
1109 "_derp_region.iroh.".try_into().unwrap(),
1110 "1.1.1.1".parse().unwrap(),
1111 30,
1112 )
1113 .sign(&keypair)
1114 .unwrap();
1115
1116 let serialized = to_allocvec(&signed_packet).unwrap();
1117 let deserialized: SignedPacket = from_bytes(&serialized).unwrap();
1118
1119 assert_eq!(deserialized, signed_packet);
1120
1121 {
1123 let mut bytes = vec![];
1124
1125 bytes.extend_from_slice(&[210, 1]);
1126 bytes.extend_from_slice(&signed_packet.last_seen().as_u64().to_le_bytes());
1127 bytes.extend_from_slice(signed_packet.as_bytes());
1128
1129 let deserialized: SignedPacket = from_bytes(&bytes).unwrap();
1130
1131 assert_eq!(deserialized.as_bytes(), signed_packet.as_bytes());
1132 assert_eq!(deserialized.last_seen(), &Timestamp::from(0));
1133 }
1134 }
1135
1136 #[test]
1137 fn wildcards() {
1138 let keypair = Keypair::random();
1139
1140 let signed_packet = SignedPacket::builder()
1141 .txt("bar.foo.".try_into().unwrap(), "_".try_into().unwrap(), 30)
1142 .txt("x.bar.foo".try_into().unwrap(), "_".try_into().unwrap(), 30)
1143 .txt("foo".try_into().unwrap(), "_".try_into().unwrap(), 60)
1144 .sign(&keypair)
1145 .unwrap();
1146
1147 assert_eq!(signed_packet.fresh_resource_records("*.foo.").count(), 1);
1148 }
1149}