1use std::collections::{HashMap, HashSet};
98use std::fmt;
99use std::net::IpAddr;
100use std::str::FromStr;
101
102use chrono::{DateTime, NaiveDateTime, Utc};
103use derive_builder::Builder;
104
105use crate::exit_policy::ExitPolicy;
106use crate::version::Version;
107use crate::{BridgeDistribution, Error};
108
109use super::{compute_digest, Descriptor, DigestEncoding, DigestHash};
110
111type RouterLineResult = (String, IpAddr, u16, Option<u16>, Option<u16>);
112
113fn is_valid_fingerprint(fingerprint: &str) -> bool {
117 fingerprint.len() == 40 && fingerprint.chars().all(|c| c.is_ascii_hexdigit())
118}
119
120fn is_valid_nickname(nickname: &str) -> bool {
124 if nickname.is_empty() || nickname.len() > 19 {
125 return false;
126 }
127 nickname.chars().all(|c| c.is_ascii_alphanumeric())
128}
129
130#[derive(Debug, Clone, PartialEq, Builder)]
184#[builder(setter(into, strip_option))]
185pub struct ServerDescriptor {
186 pub nickname: String,
188 #[builder(default)]
190 pub fingerprint: Option<String>,
191 pub address: IpAddr,
193 pub or_port: u16,
195 #[builder(default)]
197 pub socks_port: Option<u16>,
198 #[builder(default)]
200 pub dir_port: Option<u16>,
201 pub or_addresses: Vec<(IpAddr, u16, bool)>,
204 #[builder(default)]
206 pub platform: Option<Vec<u8>>,
207 #[builder(default)]
209 pub tor_version: Option<Version>,
210 #[builder(default)]
212 pub operating_system: Option<String>,
213 pub published: DateTime<Utc>,
215 #[builder(default)]
217 pub uptime: Option<u64>,
218 #[builder(default)]
220 pub contact: Option<Vec<u8>>,
221 #[builder(default)]
223 pub link_protocols: Option<Vec<String>>,
224 #[builder(default)]
226 pub circuit_protocols: Option<Vec<String>>,
227 pub bandwidth_avg: u64,
229 pub bandwidth_burst: u64,
231 pub bandwidth_observed: u64,
233 pub exit_policy: ExitPolicy,
235 #[builder(default)]
237 pub exit_policy_v6: Option<String>,
238 pub bridge_distribution: BridgeDistribution,
240 pub family: HashSet<String>,
242 pub hibernating: bool,
244 pub allow_single_hop_exits: bool,
246 pub allow_tunneled_dir_requests: bool,
248 pub extra_info_cache: bool,
250 #[builder(default)]
252 pub extra_info_digest: Option<String>,
253 #[builder(default)]
255 pub extra_info_sha256_digest: Option<String>,
256 pub is_hidden_service_dir: bool,
258 pub protocols: HashMap<String, Vec<u32>>,
261 #[builder(default)]
263 pub onion_key: Option<String>,
264 #[builder(default)]
266 pub onion_key_crosscert: Option<String>,
267 #[builder(default)]
269 pub ntor_onion_key: Option<String>,
270 #[builder(default)]
272 pub ntor_onion_key_crosscert: Option<String>,
273 #[builder(default)]
275 pub ntor_onion_key_crosscert_sign: Option<String>,
276 #[builder(default)]
278 pub signing_key: Option<String>,
279 #[builder(default)]
281 pub ed25519_certificate: Option<String>,
282 #[builder(default)]
284 pub ed25519_master_key: Option<String>,
285 #[builder(default)]
287 pub ed25519_signature: Option<String>,
288 pub signature: String,
290 raw_content: Vec<u8>,
292 unrecognized_lines: Vec<String>,
294}
295
296impl ServerDescriptor {
297 pub fn validate(&self) -> Result<(), Error> {
325 use crate::descriptor::ServerDescriptorError;
326
327 if !is_valid_nickname(&self.nickname) {
328 return Err(Error::Descriptor(
329 crate::descriptor::DescriptorError::ServerDescriptor(
330 ServerDescriptorError::InvalidNickname(self.nickname.clone()),
331 ),
332 ));
333 }
334
335 if let Some(ref fp) = self.fingerprint {
336 if !is_valid_fingerprint(fp) {
337 return Err(Error::Descriptor(
338 crate::descriptor::DescriptorError::ServerDescriptor(
339 ServerDescriptorError::InvalidFingerprint(fp.clone()),
340 ),
341 ));
342 }
343 }
344
345 if self.or_port == 0 {
346 return Err(Error::Descriptor(
347 crate::descriptor::DescriptorError::ServerDescriptor(
348 ServerDescriptorError::MissingRequiredField(
349 "or_port must be non-zero".to_string(),
350 ),
351 ),
352 ));
353 }
354
355 if self.bandwidth_burst < self.bandwidth_avg {
356 return Err(Error::Descriptor(
357 crate::descriptor::DescriptorError::ServerDescriptor(
358 ServerDescriptorError::InvalidBandwidth(format!(
359 "burst bandwidth ({}) must be >= average bandwidth ({})",
360 self.bandwidth_burst, self.bandwidth_avg
361 )),
362 ),
363 ));
364 }
365
366 if self.signature.is_empty() {
367 return Err(Error::Descriptor(
368 crate::descriptor::DescriptorError::ServerDescriptor(
369 ServerDescriptorError::MissingRequiredField("signature".to_string()),
370 ),
371 ));
372 }
373
374 for (addr, port, _) in &self.or_addresses {
375 if *port == 0 {
376 return Err(Error::Descriptor(
377 crate::descriptor::DescriptorError::ServerDescriptor(
378 ServerDescriptorError::MissingRequiredField(format!(
379 "or-address {} has invalid port 0",
380 addr
381 )),
382 ),
383 ));
384 }
385 }
386
387 for fp in &self.family {
388 let clean_fp = fp.trim_start_matches('$');
389 if !clean_fp.is_empty() && !is_valid_fingerprint(clean_fp) {
390 return Err(Error::Descriptor(
391 crate::descriptor::DescriptorError::ServerDescriptor(
392 ServerDescriptorError::InvalidFingerprint(fp.clone()),
393 ),
394 ));
395 }
396 }
397
398 Ok(())
399 }
400
401 pub fn new(
433 nickname: String,
434 address: IpAddr,
435 or_port: u16,
436 published: DateTime<Utc>,
437 signature: String,
438 ) -> Self {
439 Self {
440 nickname,
441 fingerprint: None,
442 address,
443 or_port,
444 socks_port: None,
445 dir_port: None,
446 or_addresses: Vec::new(),
447 platform: None,
448 tor_version: None,
449 operating_system: None,
450 published,
451 uptime: None,
452 contact: None,
453 link_protocols: None,
454 circuit_protocols: None,
455 bandwidth_avg: 0,
456 bandwidth_burst: 0,
457 bandwidth_observed: 0,
458 exit_policy: ExitPolicy::new(Vec::new()),
459 exit_policy_v6: None,
460 bridge_distribution: BridgeDistribution::Any,
461 family: HashSet::new(),
462 hibernating: false,
463 allow_single_hop_exits: false,
464 allow_tunneled_dir_requests: false,
465 extra_info_cache: false,
466 extra_info_digest: None,
467 extra_info_sha256_digest: None,
468 is_hidden_service_dir: false,
469 protocols: HashMap::new(),
470 onion_key: None,
471 onion_key_crosscert: None,
472 ntor_onion_key: None,
473 ntor_onion_key_crosscert: None,
474 ntor_onion_key_crosscert_sign: None,
475 signing_key: None,
476 ed25519_certificate: None,
477 ed25519_master_key: None,
478 ed25519_signature: None,
479 signature,
480 raw_content: Vec::new(),
481 unrecognized_lines: Vec::new(),
482 }
483 }
484
485 fn parse_router_line(line: &str) -> Result<RouterLineResult, Error> {
489 let parts: Vec<&str> = line.split_whitespace().collect();
490 if parts.len() < 5 {
491 return Err(Error::Parse {
492 location: "router".to_string(),
493 reason: "router line requires 5 fields".to_string(),
494 });
495 }
496
497 let nickname = parts[0].to_string();
498 if !is_valid_nickname(&nickname) {
499 return Err(Error::Parse {
500 location: "router".to_string(),
501 reason: format!("invalid nickname: {}", nickname),
502 });
503 }
504
505 let address: IpAddr = parts[1].parse().map_err(|_| Error::Parse {
506 location: "router".to_string(),
507 reason: format!("invalid address: {}", parts[1]),
508 })?;
509
510 let or_port: u16 = parts[2].parse().map_err(|_| Error::Parse {
511 location: "router".to_string(),
512 reason: format!("invalid or_port: {}", parts[2]),
513 })?;
514
515 let socks_port: Option<u16> = {
516 let port: u16 = parts[3].parse().map_err(|_| Error::Parse {
517 location: "router".to_string(),
518 reason: format!("invalid socks_port: {}", parts[3]),
519 })?;
520 if port == 0 {
521 None
522 } else {
523 Some(port)
524 }
525 };
526
527 let dir_port: Option<u16> = {
528 let port: u16 = parts[4].parse().map_err(|_| Error::Parse {
529 location: "router".to_string(),
530 reason: format!("invalid dir_port: {}", parts[4]),
531 })?;
532 if port == 0 {
533 None
534 } else {
535 Some(port)
536 }
537 };
538
539 Ok((nickname, address, or_port, socks_port, dir_port))
540 }
541
542 fn parse_bandwidth_line(line: &str) -> Result<(u64, u64, u64), Error> {
543 let parts: Vec<&str> = line.split_whitespace().collect();
544 if parts.len() < 3 {
545 return Err(Error::Parse {
546 location: "bandwidth".to_string(),
547 reason: "bandwidth line requires 3 values".to_string(),
548 });
549 }
550
551 let avg: u64 = parts[0].parse().map_err(|_| Error::Parse {
552 location: "bandwidth".to_string(),
553 reason: format!("invalid average bandwidth: {}", parts[0]),
554 })?;
555
556 let burst: u64 = parts[1].parse().map_err(|_| Error::Parse {
557 location: "bandwidth".to_string(),
558 reason: format!("invalid burst bandwidth: {}", parts[1]),
559 })?;
560
561 let observed: u64 = parts[2].parse().map_err(|_| Error::Parse {
562 location: "bandwidth".to_string(),
563 reason: format!("invalid observed bandwidth: {}", parts[2]),
564 })?;
565
566 Ok((avg, burst, observed))
567 }
568
569 fn parse_published_line(line: &str) -> Result<DateTime<Utc>, Error> {
570 let datetime =
571 NaiveDateTime::parse_from_str(line.trim(), "%Y-%m-%d %H:%M:%S").map_err(|e| {
572 Error::Parse {
573 location: "published".to_string(),
574 reason: format!("invalid datetime: {} - {}", line, e),
575 }
576 })?;
577 Ok(datetime.and_utc())
578 }
579
580 fn parse_fingerprint_line(line: &str) -> String {
581 line.replace(' ', "")
582 }
583
584 fn parse_platform_line(line: &str) -> (Option<Vec<u8>>, Option<Version>, Option<String>) {
585 let platform = line.as_bytes().to_vec();
586 let mut tor_version = None;
587 let mut operating_system = None;
588
589 if let Some(on_pos) = line.find(" on ") {
590 let version_part = &line[..on_pos];
591 operating_system = Some(line[on_pos + 4..].to_string());
592
593 if let Some(ver_start) = version_part.find(char::is_whitespace) {
594 let ver_str = version_part[ver_start..].trim();
595 if let Ok(v) = Version::parse(ver_str) {
596 tor_version = Some(v);
597 }
598 }
599 }
600
601 (Some(platform), tor_version, operating_system)
602 }
603
604 fn parse_protocols_line(line: &str) -> (Option<Vec<String>>, Option<Vec<String>>) {
605 let mut link_protocols = None;
606 let mut circuit_protocols = None;
607 let parts: Vec<&str> = line.split_whitespace().collect();
608 let mut i = 0;
609 while i < parts.len() {
610 if parts[i] == "Link" {
611 let mut protos = Vec::new();
612 i += 1;
613 while i < parts.len() && parts[i] != "Circuit" {
614 protos.push(parts[i].to_string());
615 i += 1;
616 }
617 link_protocols = Some(protos);
618 } else if parts[i] == "Circuit" {
619 let mut protos = Vec::new();
620 i += 1;
621 while i < parts.len() && parts[i] != "Link" {
622 protos.push(parts[i].to_string());
623 i += 1;
624 }
625 circuit_protocols = Some(protos);
626 } else {
627 i += 1;
628 }
629 }
630 (link_protocols, circuit_protocols)
631 }
632
633 fn parse_family_line(line: &str) -> HashSet<String> {
634 line.split_whitespace().map(|s| s.to_string()).collect()
635 }
636
637 fn parse_or_address(line: &str) -> Result<(IpAddr, u16, bool), Error> {
638 let line = line.trim();
639 if line.starts_with('[') {
640 if let Some(bracket_end) = line.find(']') {
641 let ipv6_str = &line[1..bracket_end];
642 let port_str = &line[bracket_end + 2..];
643 let addr: IpAddr = ipv6_str.parse().map_err(|_| Error::Parse {
644 location: "or-address".to_string(),
645 reason: format!("invalid IPv6 address: {}", ipv6_str),
646 })?;
647 let port: u16 = port_str.parse().map_err(|_| Error::Parse {
648 location: "or-address".to_string(),
649 reason: format!("invalid port: {}", port_str),
650 })?;
651 return Ok((addr, port, true));
652 }
653 }
654
655 if let Some(colon_pos) = line.rfind(':') {
656 let addr_str = &line[..colon_pos];
657 let port_str = &line[colon_pos + 1..];
658 let addr: IpAddr = addr_str.parse().map_err(|_| Error::Parse {
659 location: "or-address".to_string(),
660 reason: format!("invalid address: {}", addr_str),
661 })?;
662 let port: u16 = port_str.parse().map_err(|_| Error::Parse {
663 location: "or-address".to_string(),
664 reason: format!("invalid port: {}", port_str),
665 })?;
666 let is_ipv6 = addr.is_ipv6();
667 return Ok((addr, port, is_ipv6));
668 }
669
670 Err(Error::Parse {
671 location: "or-address".to_string(),
672 reason: format!("invalid or-address format: {}", line),
673 })
674 }
675
676 fn parse_extra_info_digest(line: &str) -> (Option<String>, Option<String>) {
677 let parts: Vec<&str> = line.split_whitespace().collect();
678 let sha1_digest = parts.first().map(|s| s.to_string());
679 let sha256_digest = parts.get(1).map(|s| s.to_string());
680 (sha1_digest, sha256_digest)
681 }
682
683 fn extract_pem_block(lines: &[&str], start_idx: usize) -> (String, usize) {
684 let mut block = String::new();
685 let mut idx = start_idx;
686 while idx < lines.len() {
687 let line = lines[idx];
688 block.push_str(line);
689 block.push('\n');
690 if line.starts_with("-----END ") {
691 break;
692 }
693 idx += 1;
694 }
695 (block.trim_end().to_string(), idx)
696 }
697
698 fn find_digest_content(content: &str) -> Option<&str> {
702 let start_marker = "router ";
703 let end_marker = "\nrouter-signature\n";
704 let start = content.find(start_marker)?;
705 let end = content.find(end_marker)?;
706 Some(&content[start..end + end_marker.len()])
707 }
708}
709
710impl Descriptor for ServerDescriptor {
711 fn parse(content: &str) -> Result<Self, Error> {
712 let raw_content = content.as_bytes().to_vec();
713 let lines: Vec<&str> = content.lines().collect();
714
715 let mut nickname = String::new();
716 let mut address: Option<IpAddr> = None;
717 let mut or_port: u16 = 0;
718 let mut socks_port: Option<u16> = None;
719 let mut dir_port: Option<u16> = None;
720 let mut fingerprint: Option<String> = None;
721 let mut or_addresses: Vec<(IpAddr, u16, bool)> = Vec::new();
722 let mut platform: Option<Vec<u8>> = None;
723 let mut tor_version: Option<Version> = None;
724 let mut operating_system: Option<String> = None;
725 let mut published: Option<DateTime<Utc>> = None;
726 let mut uptime: Option<u64> = None;
727 let mut contact: Option<Vec<u8>> = None;
728 let mut link_protocols: Option<Vec<String>> = None;
729 let mut circuit_protocols: Option<Vec<String>> = None;
730 let mut bandwidth_avg: u64 = 0;
731 let mut bandwidth_burst: u64 = 0;
732 let mut bandwidth_observed: u64 = 0;
733 let mut exit_policy_rules: Vec<String> = Vec::new();
734 let mut exit_policy_v6: Option<String> = None;
735 let mut bridge_distribution = BridgeDistribution::Any;
736 let mut family: HashSet<String> = HashSet::new();
737 let mut hibernating = false;
738 let mut allow_single_hop_exits = false;
739 let mut allow_tunneled_dir_requests = false;
740 let mut extra_info_cache = false;
741 let mut extra_info_digest: Option<String> = None;
742 let mut extra_info_sha256_digest: Option<String> = None;
743 let mut is_hidden_service_dir = false;
744 let mut protocols: HashMap<String, Vec<u32>> = HashMap::new();
745 let mut onion_key: Option<String> = None;
746 let mut onion_key_crosscert: Option<String> = None;
747 let mut ntor_onion_key: Option<String> = None;
748 let mut ntor_onion_key_crosscert: Option<String> = None;
749 let mut ntor_onion_key_crosscert_sign: Option<String> = None;
750 let mut signing_key: Option<String> = None;
751 let mut ed25519_certificate: Option<String> = None;
752 let mut ed25519_master_key: Option<String> = None;
753 let mut ed25519_signature: Option<String> = None;
754 let mut signature = String::new();
755 let mut unrecognized_lines: Vec<String> = Vec::new();
756
757 let mut idx = 0;
758 while idx < lines.len() {
759 let line = lines[idx];
760
761 if line.starts_with("@type ") {
762 idx += 1;
763 continue;
764 }
765
766 let line = line.strip_prefix("opt ").unwrap_or(line);
767
768 let (keyword, value) = if let Some(space_pos) = line.find(' ') {
769 (&line[..space_pos], line[space_pos + 1..].trim())
770 } else {
771 (line, "")
772 };
773
774 match keyword {
775 "router" => {
776 let (n, a, op, sp, dp) = Self::parse_router_line(value)?;
777 nickname = n;
778 address = Some(a);
779 or_port = op;
780 socks_port = sp;
781 dir_port = dp;
782 }
783 "identity-ed25519" => {
784 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
785 ed25519_certificate = Some(block);
786 idx = end_idx;
787 }
788 "master-key-ed25519" => {
789 ed25519_master_key = Some(value.to_string());
790 }
791 "bandwidth" => {
792 let (avg, burst, obs) = Self::parse_bandwidth_line(value)?;
793 bandwidth_avg = avg;
794 bandwidth_burst = burst;
795 bandwidth_observed = obs;
796 }
797 "platform" => {
798 let (p, v, os) = Self::parse_platform_line(value);
799 platform = p;
800 tor_version = v;
801 operating_system = os;
802 }
803 "published" => {
804 published = Some(Self::parse_published_line(value)?);
805 }
806 "fingerprint" => {
807 fingerprint = Some(Self::parse_fingerprint_line(value));
808 }
809 "uptime" => {
810 uptime = value.parse().ok();
811 }
812 "contact" => {
813 contact = Some(value.as_bytes().to_vec());
814 }
815 "protocols" => {
816 let (lp, cp) = Self::parse_protocols_line(value);
817 link_protocols = lp;
818 circuit_protocols = cp;
819 }
820 "proto" => {
821 for entry in value.split_whitespace() {
822 if let Some(eq_pos) = entry.find('=') {
823 let proto_name = &entry[..eq_pos];
824 let versions_str = &entry[eq_pos + 1..];
825 let versions: Vec<u32> = versions_str
826 .split(',')
827 .filter_map(|v| {
828 if let Some(dash) = v.find('-') {
829 let start: u32 = v[..dash].parse().ok()?;
830 let end: u32 = v[dash + 1..].parse().ok()?;
831 Some((start..=end).collect::<Vec<_>>())
832 } else {
833 v.parse().ok().map(|n| vec![n])
834 }
835 })
836 .flatten()
837 .collect();
838 protocols.insert(proto_name.to_string(), versions);
839 }
840 }
841 }
842 "family" => {
843 family = Self::parse_family_line(value);
844 }
845 "or-address" => {
846 if let Ok(addr) = Self::parse_or_address(value) {
847 or_addresses.push(addr);
848 }
849 }
850 "extra-info-digest" => {
851 let (sha1, sha256) = Self::parse_extra_info_digest(value);
852 extra_info_digest = sha1;
853 extra_info_sha256_digest = sha256;
854 }
855 "hidden-service-dir" => {
856 is_hidden_service_dir = true;
857 }
858 "caches-extra-info" => {
859 extra_info_cache = true;
860 }
861 "hibernating" => {
862 hibernating = value == "1";
863 }
864 "allow-single-hop-exits" => {
865 allow_single_hop_exits = true;
866 }
867 "tunnelled-dir-server" => {
868 allow_tunneled_dir_requests = true;
869 }
870 "bridge-distribution-request" => {
871 bridge_distribution = match value.to_lowercase().as_str() {
872 "https" => BridgeDistribution::Https,
873 "email" => BridgeDistribution::Email,
874 "moat" => BridgeDistribution::Moat,
875 "hyphae" => BridgeDistribution::Hyphae,
876 _ => BridgeDistribution::Any,
877 };
878 }
879 "accept" | "reject" => {
880 exit_policy_rules.push(line.to_string());
881 }
882 "ipv6-policy" => {
883 exit_policy_v6 = Some(value.to_string());
884 }
885 "onion-key" => {
886 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
887 onion_key = Some(block);
888 idx = end_idx;
889 }
890 "onion-key-crosscert" => {
891 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
892 onion_key_crosscert = Some(block);
893 idx = end_idx;
894 }
895 "ntor-onion-key" => {
896 ntor_onion_key = Some(value.to_string());
897 }
898 "ntor-onion-key-crosscert" => {
899 ntor_onion_key_crosscert_sign = Some(value.to_string());
900 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
901 ntor_onion_key_crosscert = Some(block);
902 idx = end_idx;
903 }
904 "signing-key" => {
905 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
906 signing_key = Some(block);
907 idx = end_idx;
908 }
909 "router-sig-ed25519" => {
910 ed25519_signature = Some(value.to_string());
911 }
912 "router-signature" => {
913 let (block, end_idx) = Self::extract_pem_block(&lines, idx + 1);
914 signature = block;
915 idx = end_idx;
916 }
917 _ => {
918 if !line.is_empty() && !line.starts_with("-----") {
919 unrecognized_lines.push(line.to_string());
920 }
921 }
922 }
923 idx += 1;
924 }
925
926 let address = address.ok_or_else(|| Error::Parse {
927 location: "router".to_string(),
928 reason: "missing router line".to_string(),
929 })?;
930
931 let published = published.ok_or_else(|| Error::Parse {
932 location: "published".to_string(),
933 reason: "missing published line".to_string(),
934 })?;
935
936 let exit_policy = if exit_policy_rules.is_empty() {
937 ExitPolicy::new(Vec::new())
938 } else {
939 ExitPolicy::from_rules(&exit_policy_rules)?
940 };
941
942 Ok(Self {
943 nickname,
944 fingerprint,
945 address,
946 or_port,
947 socks_port,
948 dir_port,
949 or_addresses,
950 platform,
951 tor_version,
952 operating_system,
953 published,
954 uptime,
955 contact,
956 link_protocols,
957 circuit_protocols,
958 bandwidth_avg,
959 bandwidth_burst,
960 bandwidth_observed,
961 exit_policy,
962 exit_policy_v6,
963 bridge_distribution,
964 family,
965 hibernating,
966 allow_single_hop_exits,
967 allow_tunneled_dir_requests,
968 extra_info_cache,
969 extra_info_digest,
970 extra_info_sha256_digest,
971 is_hidden_service_dir,
972 protocols,
973 onion_key,
974 onion_key_crosscert,
975 ntor_onion_key,
976 ntor_onion_key_crosscert,
977 ntor_onion_key_crosscert_sign,
978 signing_key,
979 ed25519_certificate,
980 ed25519_master_key,
981 ed25519_signature,
982 signature,
983 raw_content,
984 unrecognized_lines,
985 })
986 }
987
988 fn to_descriptor_string(&self) -> String {
989 let mut result = String::new();
990
991 result.push_str(&format!(
992 "router {} {} {} {} {}\n",
993 self.nickname,
994 self.address,
995 self.or_port,
996 self.socks_port.unwrap_or(0),
997 self.dir_port.unwrap_or(0)
998 ));
999
1000 if let Some(ref platform) = self.platform {
1001 if let Ok(s) = std::str::from_utf8(platform) {
1002 result.push_str(&format!("platform {}\n", s));
1003 }
1004 }
1005
1006 if let Some(ref link) = self.link_protocols {
1007 if let Some(ref circuit) = self.circuit_protocols {
1008 result.push_str(&format!(
1009 "protocols Link {} Circuit {}\n",
1010 link.join(" "),
1011 circuit.join(" ")
1012 ));
1013 }
1014 }
1015
1016 result.push_str(&format!(
1017 "published {}\n",
1018 self.published.format("%Y-%m-%d %H:%M:%S")
1019 ));
1020
1021 if let Some(ref fp) = self.fingerprint {
1022 let formatted: String = fp
1023 .chars()
1024 .collect::<Vec<_>>()
1025 .chunks(4)
1026 .map(|c| c.iter().collect::<String>())
1027 .collect::<Vec<_>>()
1028 .join(" ");
1029 result.push_str(&format!("fingerprint {}\n", formatted));
1030 }
1031
1032 if let Some(uptime) = self.uptime {
1033 result.push_str(&format!("uptime {}\n", uptime));
1034 }
1035
1036 result.push_str(&format!(
1037 "bandwidth {} {} {}\n",
1038 self.bandwidth_avg, self.bandwidth_burst, self.bandwidth_observed
1039 ));
1040
1041 if let Some(ref digest) = self.extra_info_digest {
1042 if let Some(ref sha256) = self.extra_info_sha256_digest {
1043 result.push_str(&format!("extra-info-digest {} {}\n", digest, sha256));
1044 } else {
1045 result.push_str(&format!("extra-info-digest {}\n", digest));
1046 }
1047 }
1048
1049 if let Some(ref key) = self.onion_key {
1050 result.push_str("onion-key\n");
1051 result.push_str(key);
1052 result.push('\n');
1053 }
1054
1055 if let Some(ref key) = self.signing_key {
1056 result.push_str("signing-key\n");
1057 result.push_str(key);
1058 result.push('\n');
1059 }
1060
1061 if !self.family.is_empty() {
1062 let family_str: Vec<&str> = self.family.iter().map(|s| s.as_str()).collect();
1063 result.push_str(&format!("family {}\n", family_str.join(" ")));
1064 }
1065
1066 if self.is_hidden_service_dir {
1067 result.push_str("hidden-service-dir\n");
1068 }
1069
1070 if let Some(ref contact) = self.contact {
1071 if let Ok(s) = std::str::from_utf8(contact) {
1072 result.push_str(&format!("contact {}\n", s));
1073 }
1074 }
1075
1076 for rule in self.exit_policy.iter() {
1077 result.push_str(&format!("{}\n", rule));
1078 }
1079
1080 result.push_str("router-signature\n");
1081 result.push_str(&self.signature);
1082 result.push('\n');
1083
1084 result
1085 }
1086
1087 fn digest(&self, hash: DigestHash, encoding: DigestEncoding) -> Result<String, Error> {
1088 let content_str = std::str::from_utf8(&self.raw_content).map_err(|_| Error::Parse {
1089 location: "digest".to_string(),
1090 reason: "invalid UTF-8 in raw content".to_string(),
1091 })?;
1092
1093 let digest_content =
1094 Self::find_digest_content(content_str).ok_or_else(|| Error::Parse {
1095 location: "digest".to_string(),
1096 reason: "could not find digest content boundaries".to_string(),
1097 })?;
1098
1099 Ok(compute_digest(digest_content.as_bytes(), hash, encoding))
1100 }
1101
1102 fn raw_content(&self) -> &[u8] {
1103 &self.raw_content
1104 }
1105
1106 fn unrecognized_lines(&self) -> &[String] {
1107 &self.unrecognized_lines
1108 }
1109}
1110
1111impl FromStr for ServerDescriptor {
1112 type Err = Error;
1113
1114 fn from_str(s: &str) -> Result<Self, Self::Err> {
1115 Self::parse(s)
1116 }
1117}
1118
1119impl fmt::Display for ServerDescriptor {
1120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1121 write!(f, "{}", self.to_descriptor_string())
1122 }
1123}
1124
1125#[cfg(test)]
1126mod tests {
1127 use super::*;
1128 use chrono::{Datelike, Timelike};
1129
1130 const EXAMPLE_DESCRIPTOR: &str = r#"@type server-descriptor 1.0
1131router caerSidi 71.35.133.197 9001 0 0
1132platform Tor 0.2.1.30 on Linux x86_64
1133opt protocols Link 1 2 Circuit 1
1134published 2012-03-01 17:15:27
1135opt fingerprint A756 9A83 B570 6AB1 B1A9 CB52 EFF7 D2D3 2E45 53EB
1136uptime 588217
1137bandwidth 153600 256000 104590
1138opt extra-info-digest D225B728768D7EA4B5587C13A7A9D22EBBEE6E66
1139onion-key
1140-----BEGIN RSA PUBLIC KEY-----
1141MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
1142skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
1143WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
1144-----END RSA PUBLIC KEY-----
1145signing-key
1146-----BEGIN RSA PUBLIC KEY-----
1147MIGJAoGBAKwvOXyztVKnuYvpTKt+nS3XIKeO8dVungi8qGoeS+6gkR6lDtGfBTjd
1148uE9UIkdAl9zi8/1Ic2wsUNHE9jiS0VgeupITGZY8YOyMJJ/xtV1cqgiWhq1dUYaq
114951TOtUogtAPgXPh4J+V8HbFFIcCzIh3qCO/xXo+DSHhv7SSif1VpAgMBAAE=
1150-----END RSA PUBLIC KEY-----
1151family $0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1 $1FD187E8F69A9B74C9202DC16A25B9E7744AB9F6 $74FB5EFA6A46DE4060431D515DC9A790E6AD9A7C $77001D8DA9BF445B0F81AA427A675F570D222E6A $B6D83EC2D9E18B0A7A33428F8CFA9C536769E209 $D2F37F46182C23AB747787FD657E680B34EAF892 $E0BD57A11F00041A9789577C53A1B784473669E4 $E5E3E9A472EAF7BE9682B86E92305DB4C71048EF
1152opt hidden-service-dir
1153contact www.atagar.com/contact
1154reject *:*
1155router-signature
1156-----BEGIN SIGNATURE-----
1157dskLSPz8beUW7bzwDjR6EVNGpyoZde83Ejvau+5F2c6cGnlu91fiZN3suE88iE6e
1158758b9ldq5eh5mapb8vuuV3uO+0Xsud7IEOqfxdkmk0GKnUX8ouru7DSIUzUL0zqq
1159Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
1160-----END SIGNATURE-----
1161"#;
1162
1163 #[test]
1164 fn test_parse_example_descriptor() {
1165 let desc = ServerDescriptor::parse(EXAMPLE_DESCRIPTOR).unwrap();
1166
1167 assert_eq!(desc.nickname, "caerSidi");
1168 assert_eq!(
1169 desc.fingerprint,
1170 Some("A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB".to_string())
1171 );
1172 assert_eq!(desc.address.to_string(), "71.35.133.197");
1173 assert_eq!(desc.or_port, 9001);
1174 assert_eq!(desc.socks_port, None);
1175 assert_eq!(desc.dir_port, None);
1176 assert_eq!(
1177 desc.platform,
1178 Some(b"Tor 0.2.1.30 on Linux x86_64".to_vec())
1179 );
1180 assert_eq!(desc.tor_version, Some(Version::parse("0.2.1.30").unwrap()));
1181 assert_eq!(desc.operating_system, Some("Linux x86_64".to_string()));
1182 assert_eq!(desc.uptime, Some(588217));
1183 assert_eq!(
1184 desc.published,
1185 NaiveDateTime::parse_from_str("2012-03-01 17:15:27", "%Y-%m-%d %H:%M:%S")
1186 .unwrap()
1187 .and_utc()
1188 );
1189 assert_eq!(desc.contact, Some(b"www.atagar.com/contact".to_vec()));
1190 assert_eq!(
1191 desc.link_protocols,
1192 Some(vec!["1".to_string(), "2".to_string()])
1193 );
1194 assert_eq!(desc.circuit_protocols, Some(vec!["1".to_string()]));
1195 assert!(desc.is_hidden_service_dir);
1196 assert!(!desc.hibernating);
1197 assert!(!desc.allow_single_hop_exits);
1198 assert!(!desc.allow_tunneled_dir_requests);
1199 assert!(!desc.extra_info_cache);
1200 assert_eq!(
1201 desc.extra_info_digest,
1202 Some("D225B728768D7EA4B5587C13A7A9D22EBBEE6E66".to_string())
1203 );
1204 assert_eq!(desc.extra_info_sha256_digest, None);
1205 assert_eq!(desc.bridge_distribution, BridgeDistribution::Any);
1206 assert_eq!(desc.family.len(), 8);
1207 assert!(desc
1208 .family
1209 .contains("$0CE3CFB1E9CC47B63EA8869813BF6FAB7D4540C1"));
1210 assert_eq!(desc.bandwidth_avg, 153600);
1211 assert_eq!(desc.bandwidth_burst, 256000);
1212 assert_eq!(desc.bandwidth_observed, 104590);
1213 assert!(desc.onion_key.is_some());
1214 assert!(desc.signing_key.is_some());
1215 assert!(desc.signature.contains("BEGIN SIGNATURE"));
1216 assert!(desc.unrecognized_lines.is_empty());
1217 }
1218
1219 #[test]
1220 fn test_parse_minimal_descriptor() {
1221 let minimal = r#"router TestRelay 192.168.1.1 9001 0 0
1222published 2023-01-01 00:00:00
1223bandwidth 1000 2000 500
1224router-signature
1225-----BEGIN SIGNATURE-----
1226test
1227-----END SIGNATURE-----
1228"#;
1229 let desc = ServerDescriptor::parse(minimal).unwrap();
1230 assert_eq!(desc.nickname, "TestRelay");
1231 assert_eq!(desc.address.to_string(), "192.168.1.1");
1232 assert_eq!(desc.or_port, 9001);
1233 assert_eq!(desc.bandwidth_avg, 1000);
1234 assert_eq!(desc.bandwidth_burst, 2000);
1235 assert_eq!(desc.bandwidth_observed, 500);
1236 }
1237
1238 #[test]
1239 fn test_parse_router_line() {
1240 let (nickname, address, or_port, socks_port, dir_port) =
1241 ServerDescriptor::parse_router_line("caerSidi 71.35.133.197 9001 0 0").unwrap();
1242 assert_eq!(nickname, "caerSidi");
1243 assert_eq!(address.to_string(), "71.35.133.197");
1244 assert_eq!(or_port, 9001);
1245 assert_eq!(socks_port, None);
1246 assert_eq!(dir_port, None);
1247 }
1248
1249 #[test]
1250 fn test_parse_router_line_with_ports() {
1251 let (nickname, address, or_port, socks_port, dir_port) =
1252 ServerDescriptor::parse_router_line("TestRelay 10.0.0.1 9001 9050 9030").unwrap();
1253 assert_eq!(nickname, "TestRelay");
1254 assert_eq!(address.to_string(), "10.0.0.1");
1255 assert_eq!(or_port, 9001);
1256 assert_eq!(socks_port, Some(9050));
1257 assert_eq!(dir_port, Some(9030));
1258 }
1259
1260 #[test]
1261 fn test_parse_bandwidth_line() {
1262 let (avg, burst, observed) =
1263 ServerDescriptor::parse_bandwidth_line("153600 256000 104590").unwrap();
1264 assert_eq!(avg, 153600);
1265 assert_eq!(burst, 256000);
1266 assert_eq!(observed, 104590);
1267 }
1268
1269 #[test]
1270 fn test_parse_published_line() {
1271 let dt = ServerDescriptor::parse_published_line("2012-03-01 17:15:27").unwrap();
1272 assert_eq!(dt.year(), 2012);
1273 assert_eq!(dt.month(), 3);
1274 assert_eq!(dt.day(), 1);
1275 assert_eq!(dt.hour(), 17);
1276 assert_eq!(dt.minute(), 15);
1277 assert_eq!(dt.second(), 27);
1278 }
1279
1280 #[test]
1281 fn test_parse_fingerprint_line() {
1282 let fp = ServerDescriptor::parse_fingerprint_line(
1283 "A756 9A83 B570 6AB1 B1A9 CB52 EFF7 D2D3 2E45 53EB",
1284 );
1285 assert_eq!(fp, "A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB");
1286 }
1287
1288 #[test]
1289 fn test_parse_platform_line() {
1290 let (platform, version, os) =
1291 ServerDescriptor::parse_platform_line("Tor 0.2.1.30 on Linux x86_64");
1292 assert_eq!(platform, Some(b"Tor 0.2.1.30 on Linux x86_64".to_vec()));
1293 assert_eq!(version, Some(Version::parse("0.2.1.30").unwrap()));
1294 assert_eq!(os, Some("Linux x86_64".to_string()));
1295 }
1296
1297 #[test]
1298 fn test_parse_protocols_line() {
1299 let (link, circuit) = ServerDescriptor::parse_protocols_line("Link 1 2 Circuit 1");
1300 assert_eq!(link, Some(vec!["1".to_string(), "2".to_string()]));
1301 assert_eq!(circuit, Some(vec!["1".to_string()]));
1302 }
1303
1304 #[test]
1305 fn test_parse_family_line() {
1306 let family = ServerDescriptor::parse_family_line("$ABC123 $DEF456 $GHI789");
1307 assert_eq!(family.len(), 3);
1308 assert!(family.contains("$ABC123"));
1309 assert!(family.contains("$DEF456"));
1310 assert!(family.contains("$GHI789"));
1311 }
1312
1313 #[test]
1314 fn test_parse_or_address_ipv4() {
1315 let (addr, port, is_ipv6) = ServerDescriptor::parse_or_address("192.168.1.1:9001").unwrap();
1316 assert_eq!(addr.to_string(), "192.168.1.1");
1317 assert_eq!(port, 9001);
1318 assert!(!is_ipv6);
1319 }
1320
1321 #[test]
1322 fn test_parse_or_address_ipv6() {
1323 let (addr, port, is_ipv6) =
1324 ServerDescriptor::parse_or_address("[2001:db8::1]:9001").unwrap();
1325 assert_eq!(addr.to_string(), "2001:db8::1");
1326 assert_eq!(port, 9001);
1327 assert!(is_ipv6);
1328 }
1329
1330 #[test]
1331 fn test_invalid_nickname_too_long() {
1332 let result = ServerDescriptor::parse_router_line(
1333 "ThisNicknameIsWayTooLongToBeValid 192.168.1.1 9001 0 0",
1334 );
1335 assert!(result.is_err());
1336 }
1337
1338 #[test]
1339 fn test_invalid_nickname_special_chars() {
1340 let result = ServerDescriptor::parse_router_line("Invalid$Name 192.168.1.1 9001 0 0");
1341 assert!(result.is_err());
1342 }
1343
1344 #[test]
1345 fn test_invalid_address() {
1346 let result = ServerDescriptor::parse_router_line("TestRelay 999.999.999.999 9001 0 0");
1347 assert!(result.is_err());
1348 }
1349
1350 #[test]
1351 fn test_invalid_port() {
1352 let result = ServerDescriptor::parse_router_line("TestRelay 192.168.1.1 99999 0 0");
1353 assert!(result.is_err());
1354 }
1355
1356 #[test]
1357 fn test_digest_sha1() {
1358 let desc = ServerDescriptor::parse(EXAMPLE_DESCRIPTOR).unwrap();
1359 let digest = desc.digest(DigestHash::Sha1, DigestEncoding::Hex).unwrap();
1360 assert_eq!(digest.len(), 40);
1361 assert!(digest.chars().all(|c| c.is_ascii_hexdigit()));
1362 }
1363
1364 #[test]
1365 fn test_digest_sha256() {
1366 let desc = ServerDescriptor::parse(EXAMPLE_DESCRIPTOR).unwrap();
1367 let digest = desc
1368 .digest(DigestHash::Sha256, DigestEncoding::Hex)
1369 .unwrap();
1370 assert_eq!(digest.len(), 64);
1371 assert!(digest.chars().all(|c| c.is_ascii_hexdigit()));
1372 }
1373
1374 #[test]
1375 fn test_to_descriptor_string() {
1376 let desc = ServerDescriptor::parse(EXAMPLE_DESCRIPTOR).unwrap();
1377 let output = desc.to_descriptor_string();
1378 assert!(output.contains("router caerSidi 71.35.133.197 9001 0 0"));
1379 assert!(output.contains("bandwidth 153600 256000 104590"));
1380 assert!(output.contains("router-signature"));
1381 }
1382
1383 #[test]
1384 fn test_descriptor_with_exit_policy() {
1385 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1386published 2023-01-01 00:00:00
1387bandwidth 1000 2000 500
1388accept *:80
1389accept *:443
1390reject *:*
1391router-signature
1392-----BEGIN SIGNATURE-----
1393test
1394-----END SIGNATURE-----
1395"#;
1396 let desc = ServerDescriptor::parse(content).unwrap();
1397 assert!(desc
1398 .exit_policy
1399 .can_exit_to("10.0.0.1".parse().unwrap(), 80));
1400 assert!(desc
1401 .exit_policy
1402 .can_exit_to("10.0.0.1".parse().unwrap(), 443));
1403 assert!(!desc
1404 .exit_policy
1405 .can_exit_to("10.0.0.1".parse().unwrap(), 22));
1406 }
1407
1408 #[test]
1409 fn test_descriptor_with_hibernating() {
1410 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1411published 2023-01-01 00:00:00
1412bandwidth 1000 2000 500
1413hibernating 1
1414router-signature
1415-----BEGIN SIGNATURE-----
1416test
1417-----END SIGNATURE-----
1418"#;
1419 let desc = ServerDescriptor::parse(content).unwrap();
1420 assert!(desc.hibernating);
1421 }
1422
1423 #[test]
1424 fn test_descriptor_with_proto() {
1425 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1426published 2023-01-01 00:00:00
1427bandwidth 1000 2000 500
1428proto Cons=1-2 Desc=1-2 DirCache=1-2 HSDir=1-2 HSIntro=3-4 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 Relay=1-2
1429router-signature
1430-----BEGIN SIGNATURE-----
1431test
1432-----END SIGNATURE-----
1433"#;
1434 let desc = ServerDescriptor::parse(content).unwrap();
1435 assert!(desc.protocols.contains_key("Cons"));
1436 assert!(desc.protocols.contains_key("Link"));
1437 assert_eq!(desc.protocols.get("Cons"), Some(&vec![1, 2]));
1438 }
1439
1440 #[test]
1441 fn test_is_valid_nickname() {
1442 assert!(is_valid_nickname("caerSidi"));
1443 assert!(is_valid_nickname("TestRelay123"));
1444 assert!(is_valid_nickname("A"));
1445 assert!(is_valid_nickname("ABCDEFGHIJKLMNOPQRS"));
1446 assert!(!is_valid_nickname(""));
1447 assert!(!is_valid_nickname("ABCDEFGHIJKLMNOPQRST"));
1448 assert!(!is_valid_nickname("Invalid$Name"));
1449 assert!(!is_valid_nickname("Invalid Name"));
1450 }
1451}
1452
1453#[cfg(test)]
1454mod proptests {
1455 use super::*;
1456 use proptest::prelude::*;
1457
1458 fn valid_nickname() -> impl Strategy<Value = String> {
1459 "[a-zA-Z][a-zA-Z0-9]{0,18}".prop_filter("must be valid nickname", |s| {
1460 !s.is_empty() && s.len() <= 19 && s.chars().all(|c| c.is_ascii_alphanumeric())
1461 })
1462 }
1463
1464 fn valid_ipv4() -> impl Strategy<Value = IpAddr> {
1465 (1u8..255, 0u8..255, 0u8..255, 1u8..255)
1466 .prop_map(|(a, b, c, d)| IpAddr::V4(std::net::Ipv4Addr::new(a, b, c, d)))
1467 }
1468
1469 fn valid_port() -> impl Strategy<Value = u16> {
1470 1u16..65535
1471 }
1472
1473 fn valid_bandwidth() -> impl Strategy<Value = u64> {
1474 1u64..1_000_000_000
1475 }
1476
1477 fn valid_datetime() -> impl Strategy<Value = DateTime<Utc>> {
1478 (
1479 2000i32..2030,
1480 1u32..13,
1481 1u32..29,
1482 0u32..24,
1483 0u32..60,
1484 0u32..60,
1485 )
1486 .prop_map(|(year, month, day, hour, min, sec)| {
1487 NaiveDateTime::new(
1488 chrono::NaiveDate::from_ymd_opt(year, month, day).unwrap(),
1489 chrono::NaiveTime::from_hms_opt(hour, min, sec).unwrap(),
1490 )
1491 .and_utc()
1492 })
1493 }
1494
1495 fn valid_fingerprint() -> impl Strategy<Value = String> {
1496 proptest::collection::vec(
1497 proptest::char::range('0', '9').prop_union(proptest::char::range('A', 'F')),
1498 40..=40,
1499 )
1500 .prop_map(|chars| chars.into_iter().collect())
1501 }
1502
1503 fn simple_server_descriptor() -> impl Strategy<Value = ServerDescriptor> {
1504 (
1505 valid_nickname(),
1506 valid_ipv4(),
1507 valid_port(),
1508 valid_datetime(),
1509 valid_bandwidth(),
1510 valid_bandwidth(),
1511 valid_bandwidth(),
1512 proptest::option::of(valid_fingerprint()),
1513 )
1514 .prop_map(
1515 |(nickname, address, or_port, published, bw_avg, bw_burst, bw_obs, fingerprint)| {
1516 let mut desc = ServerDescriptor::new(
1517 nickname,
1518 address,
1519 or_port,
1520 published,
1521 "-----BEGIN SIGNATURE-----\ntest\n-----END SIGNATURE-----".to_string(),
1522 );
1523 desc.bandwidth_avg = bw_avg;
1524 desc.bandwidth_burst = bw_burst;
1525 desc.bandwidth_observed = bw_obs;
1526 desc.fingerprint = fingerprint;
1527 desc
1528 },
1529 )
1530 }
1531
1532 proptest! {
1533 #![proptest_config(ProptestConfig::with_cases(100))]
1534
1535 #[test]
1536 fn prop_server_descriptor_roundtrip(desc in simple_server_descriptor()) {
1537 let serialized = desc.to_descriptor_string();
1538 let parsed = ServerDescriptor::parse(&serialized);
1539
1540 prop_assert!(parsed.is_ok(), "Failed to parse serialized descriptor: {:?}", parsed.err());
1541
1542 let parsed = parsed.unwrap();
1543
1544 prop_assert_eq!(&desc.nickname, &parsed.nickname, "nickname mismatch");
1545 prop_assert_eq!(desc.address, parsed.address, "address mismatch");
1546 prop_assert_eq!(desc.or_port, parsed.or_port, "or_port mismatch");
1547 prop_assert_eq!(desc.socks_port, parsed.socks_port, "socks_port mismatch");
1548 prop_assert_eq!(desc.dir_port, parsed.dir_port, "dir_port mismatch");
1549
1550 prop_assert_eq!(desc.bandwidth_avg, parsed.bandwidth_avg, "bandwidth_avg mismatch");
1551 prop_assert_eq!(desc.bandwidth_burst, parsed.bandwidth_burst, "bandwidth_burst mismatch");
1552 prop_assert_eq!(desc.bandwidth_observed, parsed.bandwidth_observed, "bandwidth_observed mismatch");
1553
1554 prop_assert_eq!(desc.published, parsed.published, "published mismatch");
1555
1556 prop_assert_eq!(desc.fingerprint, parsed.fingerprint, "fingerprint mismatch");
1557 }
1558
1559 #[test]
1560 fn prop_valid_nickname_parsing(nickname in valid_nickname()) {
1561 let content = format!(
1562 "router {} 192.168.1.1 9001 0 0\npublished 2023-01-01 00:00:00\nbandwidth 1000 2000 500\nrouter-signature\n-----BEGIN SIGNATURE-----\ntest\n-----END SIGNATURE-----\n",
1563 nickname
1564 );
1565 let result = ServerDescriptor::parse(&content);
1566 prop_assert!(result.is_ok(), "Failed to parse descriptor with nickname '{}': {:?}", nickname, result.err());
1567 prop_assert_eq!(result.unwrap().nickname, nickname);
1568 }
1569
1570 #[test]
1571 fn prop_bandwidth_preserved(
1572 avg in valid_bandwidth(),
1573 burst in valid_bandwidth(),
1574 observed in valid_bandwidth()
1575 ) {
1576 let content = format!(
1577 "router TestRelay 192.168.1.1 9001 0 0\npublished 2023-01-01 00:00:00\nbandwidth {} {} {}\nrouter-signature\n-----BEGIN SIGNATURE-----\ntest\n-----END SIGNATURE-----\n",
1578 avg, burst, observed
1579 );
1580 let result = ServerDescriptor::parse(&content);
1581 prop_assert!(result.is_ok());
1582 let desc = result.unwrap();
1583 prop_assert_eq!(desc.bandwidth_avg, avg);
1584 prop_assert_eq!(desc.bandwidth_burst, burst);
1585 prop_assert_eq!(desc.bandwidth_observed, observed);
1586 }
1587
1588 #[test]
1589 fn prop_ports_preserved(
1590 or_port in valid_port(),
1591 socks_port in proptest::option::of(valid_port()),
1592 dir_port in proptest::option::of(valid_port())
1593 ) {
1594 let socks = socks_port.unwrap_or(0);
1595 let dir = dir_port.unwrap_or(0);
1596 let content = format!(
1597 "router TestRelay 192.168.1.1 {} {} {}\npublished 2023-01-01 00:00:00\nbandwidth 1000 2000 500\nrouter-signature\n-----BEGIN SIGNATURE-----\ntest\n-----END SIGNATURE-----\n",
1598 or_port, socks, dir
1599 );
1600 let result = ServerDescriptor::parse(&content);
1601 prop_assert!(result.is_ok());
1602 let desc = result.unwrap();
1603 prop_assert_eq!(desc.or_port, or_port);
1604 prop_assert_eq!(desc.socks_port, if socks == 0 { None } else { Some(socks) });
1605 prop_assert_eq!(desc.dir_port, if dir == 0 { None } else { Some(dir) });
1606 }
1607
1608 #[test]
1609 fn prop_fingerprint_format_preserved(fp in valid_fingerprint()) {
1610 let formatted: String = fp
1611 .chars()
1612 .collect::<Vec<_>>()
1613 .chunks(4)
1614 .map(|c| c.iter().collect::<String>())
1615 .collect::<Vec<_>>()
1616 .join(" ");
1617
1618 let content = format!(
1619 "router TestRelay 192.168.1.1 9001 0 0\npublished 2023-01-01 00:00:00\nfingerprint {}\nbandwidth 1000 2000 500\nrouter-signature\n-----BEGIN SIGNATURE-----\ntest\n-----END SIGNATURE-----\n",
1620 formatted
1621 );
1622 let result = ServerDescriptor::parse(&content);
1623 prop_assert!(result.is_ok());
1624 let desc = result.unwrap();
1625 prop_assert_eq!(desc.fingerprint, Some(fp));
1626 }
1627 }
1628
1629 #[test]
1630 fn test_edge_case_empty_family() {
1631 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1632published 2023-01-01 00:00:00
1633bandwidth 1000 2000 500
1634family
1635router-signature
1636-----BEGIN SIGNATURE-----
1637test
1638-----END SIGNATURE-----
1639"#;
1640 let desc = ServerDescriptor::parse(content).unwrap();
1641 assert!(desc.family.is_empty());
1642 }
1643
1644 #[test]
1645 fn test_edge_case_zero_bandwidth() {
1646 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1647published 2023-01-01 00:00:00
1648bandwidth 0 0 0
1649router-signature
1650-----BEGIN SIGNATURE-----
1651test
1652-----END SIGNATURE-----
1653"#;
1654 let desc = ServerDescriptor::parse(content).unwrap();
1655 assert_eq!(desc.bandwidth_avg, 0);
1656 assert_eq!(desc.bandwidth_burst, 0);
1657 assert_eq!(desc.bandwidth_observed, 0);
1658 }
1659
1660 #[test]
1661 fn test_edge_case_large_bandwidth() {
1662 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1663published 2023-01-01 00:00:00
1664bandwidth 9223372036854775807 9223372036854775807 9223372036854775807
1665router-signature
1666-----BEGIN SIGNATURE-----
1667test
1668-----END SIGNATURE-----
1669"#;
1670 let desc = ServerDescriptor::parse(content).unwrap();
1671 assert_eq!(desc.bandwidth_avg, i64::MAX as u64);
1672 assert_eq!(desc.bandwidth_burst, i64::MAX as u64);
1673 assert_eq!(desc.bandwidth_observed, i64::MAX as u64);
1674 }
1675
1676 #[test]
1677 fn test_edge_case_zero_uptime() {
1678 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1679published 2023-01-01 00:00:00
1680bandwidth 1000 2000 500
1681uptime 0
1682router-signature
1683-----BEGIN SIGNATURE-----
1684test
1685-----END SIGNATURE-----
1686"#;
1687 let desc = ServerDescriptor::parse(content).unwrap();
1688 assert_eq!(desc.uptime, Some(0));
1689 }
1690
1691 #[test]
1692 fn test_edge_case_large_uptime() {
1693 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1694published 2023-01-01 00:00:00
1695bandwidth 1000 2000 500
1696uptime 4294967295
1697router-signature
1698-----BEGIN SIGNATURE-----
1699test
1700-----END SIGNATURE-----
1701"#;
1702 let desc = ServerDescriptor::parse(content).unwrap();
1703 assert_eq!(desc.uptime, Some(4294967295));
1704 }
1705
1706 #[test]
1707 fn test_edge_case_empty_contact() {
1708 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1709published 2023-01-01 00:00:00
1710bandwidth 1000 2000 500
1711contact
1712router-signature
1713-----BEGIN SIGNATURE-----
1714test
1715-----END SIGNATURE-----
1716"#;
1717 let desc = ServerDescriptor::parse(content).unwrap();
1718 assert_eq!(desc.contact, Some(Vec::new()));
1719 }
1720
1721 #[test]
1722 fn test_edge_case_multiple_or_addresses() {
1723 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1724published 2023-01-01 00:00:00
1725bandwidth 1000 2000 500
1726or-address 10.0.0.1:9002
1727or-address [2001:db8::1]:9003
1728or-address 172.16.0.1:9004
1729router-signature
1730-----BEGIN SIGNATURE-----
1731test
1732-----END SIGNATURE-----
1733"#;
1734 let desc = ServerDescriptor::parse(content).unwrap();
1735 assert_eq!(desc.or_addresses.len(), 3);
1736 assert_eq!(desc.or_addresses[0].0.to_string(), "10.0.0.1");
1737 assert_eq!(desc.or_addresses[0].1, 9002);
1738 assert_eq!(desc.or_addresses[1].0.to_string(), "2001:db8::1");
1739 assert_eq!(desc.or_addresses[1].1, 9003);
1740 assert_eq!(desc.or_addresses[2].0.to_string(), "172.16.0.1");
1741 assert_eq!(desc.or_addresses[2].1, 9004);
1742 }
1743
1744 #[test]
1745 fn test_edge_case_all_bridge_distributions() {
1746 for (dist_str, expected) in [
1747 ("https", BridgeDistribution::Https),
1748 ("email", BridgeDistribution::Email),
1749 ("moat", BridgeDistribution::Moat),
1750 ("hyphae", BridgeDistribution::Hyphae),
1751 ("any", BridgeDistribution::Any),
1752 ("unknown", BridgeDistribution::Any),
1753 ] {
1754 let content = format!(
1755 r#"router TestRelay 192.168.1.1 9001 0 0
1756published 2023-01-01 00:00:00
1757bandwidth 1000 2000 500
1758bridge-distribution-request {}
1759router-signature
1760-----BEGIN SIGNATURE-----
1761test
1762-----END SIGNATURE-----
1763"#,
1764 dist_str
1765 );
1766 let desc = ServerDescriptor::parse(&content).unwrap();
1767 assert_eq!(desc.bridge_distribution, expected);
1768 }
1769 }
1770
1771 #[test]
1772 fn test_edge_case_proto_with_ranges() {
1773 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1774published 2023-01-01 00:00:00
1775bandwidth 1000 2000 500
1776proto Link=1-5 Circuit=1-2 Relay=1-3
1777router-signature
1778-----BEGIN SIGNATURE-----
1779test
1780-----END SIGNATURE-----
1781"#;
1782 let desc = ServerDescriptor::parse(content).unwrap();
1783 assert_eq!(desc.protocols.get("Link"), Some(&vec![1, 2, 3, 4, 5]));
1784 assert_eq!(desc.protocols.get("Circuit"), Some(&vec![1, 2]));
1785 assert_eq!(desc.protocols.get("Relay"), Some(&vec![1, 2, 3]));
1786 }
1787
1788 #[test]
1789 fn test_edge_case_proto_with_mixed_ranges() {
1790 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1791published 2023-01-01 00:00:00
1792bandwidth 1000 2000 500
1793proto Link=1-2,4,6-8
1794router-signature
1795-----BEGIN SIGNATURE-----
1796test
1797-----END SIGNATURE-----
1798"#;
1799 let desc = ServerDescriptor::parse(content).unwrap();
1800 assert_eq!(desc.protocols.get("Link"), Some(&vec![1, 2, 4, 6, 7, 8]));
1801 }
1802
1803 #[test]
1804 fn test_edge_case_unrecognized_lines() {
1805 let content = r#"router TestRelay 192.168.1.1 9001 0 0
1806published 2023-01-01 00:00:00
1807bandwidth 1000 2000 500
1808unknown-keyword some value
1809another-unknown-line with multiple words
1810router-signature
1811-----BEGIN SIGNATURE-----
1812test
1813-----END SIGNATURE-----
1814"#;
1815 let desc = ServerDescriptor::parse(content).unwrap();
1816 assert_eq!(desc.unrecognized_lines.len(), 2);
1817 assert!(desc
1818 .unrecognized_lines
1819 .contains(&"unknown-keyword some value".to_string()));
1820 assert!(desc
1821 .unrecognized_lines
1822 .contains(&"another-unknown-line with multiple words".to_string()));
1823 }
1824
1825 #[test]
1826 fn test_roundtrip_with_all_fields() {
1827 let content = r#"router TestRelay 192.168.1.1 9001 9050 9030
1828platform Tor 0.4.7.10 on Linux x86_64
1829protocols Link 1 2 Circuit 1
1830published 2023-01-01 12:00:00
1831fingerprint AAAA BBBB CCCC DDDD EEEE FFFF 0000 1111 2222 3333
1832uptime 86400
1833bandwidth 1000000 2000000 500000
1834extra-info-digest ABC123 DEF456
1835family $1111111111111111111111111111111111111111 $2222222222222222222222222222222222222222
1836hidden-service-dir
1837contact admin@example.com
1838accept *:80
1839accept *:443
1840reject *:*
1841router-signature
1842-----BEGIN SIGNATURE-----
1843test
1844-----END SIGNATURE-----
1845"#;
1846 let desc = ServerDescriptor::parse(content).unwrap();
1847 let serialized = desc.to_descriptor_string();
1848 let reparsed = ServerDescriptor::parse(&serialized).unwrap();
1849
1850 assert_eq!(desc.nickname, reparsed.nickname);
1851 assert_eq!(desc.address, reparsed.address);
1852 assert_eq!(desc.or_port, reparsed.or_port);
1853 assert_eq!(desc.socks_port, reparsed.socks_port);
1854 assert_eq!(desc.dir_port, reparsed.dir_port);
1855 assert_eq!(desc.published, reparsed.published);
1856 assert_eq!(desc.fingerprint, reparsed.fingerprint);
1857 assert_eq!(desc.uptime, reparsed.uptime);
1858 assert_eq!(desc.bandwidth_avg, reparsed.bandwidth_avg);
1859 assert_eq!(desc.bandwidth_burst, reparsed.bandwidth_burst);
1860 assert_eq!(desc.bandwidth_observed, reparsed.bandwidth_observed);
1861 assert_eq!(desc.family, reparsed.family);
1862 assert_eq!(desc.is_hidden_service_dir, reparsed.is_hidden_service_dir);
1863 }
1864
1865 #[test]
1866 fn test_validation_invalid_nickname_empty() {
1867 let desc = ServerDescriptor::new(
1868 String::new(),
1869 "192.168.1.1".parse().unwrap(),
1870 9001,
1871 Utc::now(),
1872 "test".to_string(),
1873 );
1874 let result = desc.validate();
1875 assert!(result.is_err());
1876 }
1877
1878 #[test]
1879 fn test_validation_invalid_nickname_too_long() {
1880 let desc = ServerDescriptor::new(
1881 "ThisNicknameIsWayTooLongToBeValid".to_string(),
1882 "192.168.1.1".parse().unwrap(),
1883 9001,
1884 Utc::now(),
1885 "test".to_string(),
1886 );
1887 let result = desc.validate();
1888 assert!(result.is_err());
1889 }
1890
1891 #[test]
1892 fn test_validation_invalid_nickname_special_chars() {
1893 let desc = ServerDescriptor::new(
1894 "Invalid$Name".to_string(),
1895 "192.168.1.1".parse().unwrap(),
1896 9001,
1897 Utc::now(),
1898 "test".to_string(),
1899 );
1900 let result = desc.validate();
1901 assert!(result.is_err());
1902 }
1903
1904 #[test]
1905 fn test_validation_invalid_fingerprint() {
1906 let mut desc = ServerDescriptor::new(
1907 "TestRelay".to_string(),
1908 "192.168.1.1".parse().unwrap(),
1909 9001,
1910 Utc::now(),
1911 "test".to_string(),
1912 );
1913 desc.fingerprint = Some("INVALID".to_string());
1914 let result = desc.validate();
1915 assert!(result.is_err());
1916 }
1917
1918 #[test]
1919 fn test_validation_zero_or_port() {
1920 let desc = ServerDescriptor::new(
1921 "TestRelay".to_string(),
1922 "192.168.1.1".parse().unwrap(),
1923 0,
1924 Utc::now(),
1925 "test".to_string(),
1926 );
1927 let result = desc.validate();
1928 assert!(result.is_err());
1929 }
1930
1931 #[test]
1932 fn test_validation_burst_less_than_avg() {
1933 let mut desc = ServerDescriptor::new(
1934 "TestRelay".to_string(),
1935 "192.168.1.1".parse().unwrap(),
1936 9001,
1937 Utc::now(),
1938 "test".to_string(),
1939 );
1940 desc.bandwidth_avg = 2000;
1941 desc.bandwidth_burst = 1000;
1942 let result = desc.validate();
1943 assert!(result.is_err());
1944 }
1945
1946 #[test]
1947 fn test_validation_empty_signature() {
1948 let desc = ServerDescriptor::new(
1949 "TestRelay".to_string(),
1950 "192.168.1.1".parse().unwrap(),
1951 9001,
1952 Utc::now(),
1953 String::new(),
1954 );
1955 let result = desc.validate();
1956 assert!(result.is_err());
1957 }
1958
1959 #[test]
1960 fn test_validation_zero_port_in_or_addresses() {
1961 let mut desc = ServerDescriptor::new(
1962 "TestRelay".to_string(),
1963 "192.168.1.1".parse().unwrap(),
1964 9001,
1965 Utc::now(),
1966 "test".to_string(),
1967 );
1968 desc.or_addresses
1969 .push(("10.0.0.1".parse().unwrap(), 0, false));
1970 let result = desc.validate();
1971 assert!(result.is_err());
1972 }
1973
1974 #[test]
1975 fn test_validation_invalid_family_fingerprint() {
1976 let mut desc = ServerDescriptor::new(
1977 "TestRelay".to_string(),
1978 "192.168.1.1".parse().unwrap(),
1979 9001,
1980 Utc::now(),
1981 "test".to_string(),
1982 );
1983 desc.family.insert("$INVALID".to_string());
1984 let result = desc.validate();
1985 assert!(result.is_err());
1986 }
1987
1988 #[test]
1989 fn test_validation_valid_descriptor() {
1990 let mut desc = ServerDescriptor::new(
1991 "TestRelay".to_string(),
1992 "192.168.1.1".parse().unwrap(),
1993 9001,
1994 Utc::now(),
1995 "test".to_string(),
1996 );
1997 desc.fingerprint = Some("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_string());
1998 desc.bandwidth_avg = 1000;
1999 desc.bandwidth_burst = 2000;
2000 desc.bandwidth_observed = 500;
2001 desc.family
2002 .insert("$1111111111111111111111111111111111111111".to_string());
2003 let result = desc.validate();
2004 assert!(result.is_ok());
2005 }
2006
2007 #[test]
2008 fn test_builder_pattern() {
2009 let desc = ServerDescriptorBuilder::default()
2010 .nickname("TestRelay")
2011 .address("192.168.1.1".parse::<IpAddr>().unwrap())
2012 .or_port(9001_u16)
2013 .or_addresses(vec![])
2014 .published(Utc::now())
2015 .bandwidth_avg(1000_u64)
2016 .bandwidth_burst(2000_u64)
2017 .bandwidth_observed(500_u64)
2018 .exit_policy(ExitPolicy::new(Vec::new()))
2019 .bridge_distribution(BridgeDistribution::Any)
2020 .family(HashSet::new())
2021 .hibernating(false)
2022 .allow_single_hop_exits(false)
2023 .allow_tunneled_dir_requests(false)
2024 .extra_info_cache(false)
2025 .is_hidden_service_dir(false)
2026 .protocols(HashMap::new())
2027 .signature("test")
2028 .raw_content(vec![])
2029 .unrecognized_lines(vec![])
2030 .build()
2031 .expect("Failed to build");
2032
2033 assert_eq!(desc.nickname, "TestRelay");
2034 assert_eq!(desc.or_port, 9001);
2035 assert_eq!(desc.bandwidth_avg, 1000);
2036 }
2037
2038 #[test]
2039 fn test_builder_with_optional_fields() {
2040 let desc = ServerDescriptorBuilder::default()
2041 .nickname("TestRelay")
2042 .address("192.168.1.1".parse::<IpAddr>().unwrap())
2043 .or_port(9001_u16)
2044 .or_addresses(vec![])
2045 .published(Utc::now())
2046 .bandwidth_avg(1000_u64)
2047 .bandwidth_burst(2000_u64)
2048 .bandwidth_observed(500_u64)
2049 .exit_policy(ExitPolicy::new(Vec::new()))
2050 .bridge_distribution(BridgeDistribution::Any)
2051 .family(HashSet::new())
2052 .hibernating(false)
2053 .allow_single_hop_exits(false)
2054 .allow_tunneled_dir_requests(false)
2055 .extra_info_cache(false)
2056 .is_hidden_service_dir(false)
2057 .protocols(HashMap::new())
2058 .signature("test")
2059 .raw_content(vec![])
2060 .unrecognized_lines(vec![])
2061 .fingerprint("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
2062 .dir_port(9030_u16)
2063 .uptime(86400_u64)
2064 .build()
2065 .expect("Failed to build");
2066
2067 assert_eq!(
2068 desc.fingerprint,
2069 Some("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".to_string())
2070 );
2071 assert_eq!(desc.dir_port, Some(9030));
2072 assert_eq!(desc.uptime, Some(86400));
2073 }
2074
2075 #[test]
2076 fn test_display_implementation() {
2077 let desc = ServerDescriptor::new(
2078 "TestRelay".to_string(),
2079 "192.168.1.1".parse().unwrap(),
2080 9001,
2081 Utc::now(),
2082 "test".to_string(),
2083 );
2084 let display_str = format!("{}", desc);
2085 assert!(display_str.contains("router TestRelay 192.168.1.1 9001"));
2086 assert!(display_str.contains("router-signature"));
2087 }
2088
2089 #[test]
2090 fn test_from_str_implementation() {
2091 let content = r#"router TestRelay 192.168.1.1 9001 0 0
2092published 2023-01-01 00:00:00
2093bandwidth 1000 2000 500
2094router-signature
2095-----BEGIN SIGNATURE-----
2096test
2097-----END SIGNATURE-----
2098"#;
2099 let desc: ServerDescriptor = content.parse().unwrap();
2100 assert_eq!(desc.nickname, "TestRelay");
2101 assert_eq!(desc.or_port, 9001);
2102 }
2103}