1 #[cfg(test)] 2 mod external_ip_mapper_test; 3 4 use crate::candidate::*; 5 use crate::error::*; 6 7 use std::collections::HashMap; 8 use std::net::IpAddr; 9 10 pub(crate) fn validate_ip_string(ip_str: &str) -> Result<IpAddr> { 11 match ip_str.parse() { 12 Ok(ip) => Ok(ip), 13 Err(_) => Err(Error::ErrInvalidNat1to1IpMapping), 14 } 15 } 16 17 /// Holds the mapping of local and external IP address for a particular IP family. 18 #[derive(Default, PartialEq, Debug)] 19 pub(crate) struct IpMapping { 20 ip_sole: Option<IpAddr>, // when non-nil, this is the sole external IP for one local IP assumed 21 ip_map: HashMap<String, IpAddr>, // local-to-external IP mapping (k: local, v: external) 22 } 23 24 impl IpMapping { 25 pub(crate) fn set_sole_ip(&mut self, ip: IpAddr) -> Result<()> { 26 if self.ip_sole.is_some() || !self.ip_map.is_empty() { 27 return Err(Error::ErrInvalidNat1to1IpMapping); 28 } 29 30 self.ip_sole = Some(ip); 31 32 Ok(()) 33 } 34 35 pub(crate) fn add_ip_mapping(&mut self, loc_ip: IpAddr, ext_ip: IpAddr) -> Result<()> { 36 if self.ip_sole.is_some() { 37 return Err(Error::ErrInvalidNat1to1IpMapping); 38 } 39 40 let loc_ip_str = loc_ip.to_string(); 41 42 // check if dup of local IP 43 if self.ip_map.contains_key(&loc_ip_str) { 44 return Err(Error::ErrInvalidNat1to1IpMapping); 45 } 46 47 self.ip_map.insert(loc_ip_str, ext_ip); 48 49 Ok(()) 50 } 51 52 pub(crate) fn find_external_ip(&self, loc_ip: IpAddr) -> Result<IpAddr> { 53 if let Some(ip_sole) = &self.ip_sole { 54 return Ok(*ip_sole); 55 } 56 57 self.ip_map.get(&loc_ip.to_string()).map_or_else( 58 || Err(Error::ErrExternalMappedIpNotFound), 59 |ext_ip| Ok(*ext_ip), 60 ) 61 } 62 } 63 64 #[derive(Default)] 65 pub(crate) struct ExternalIpMapper { 66 pub(crate) ipv4_mapping: IpMapping, 67 pub(crate) ipv6_mapping: IpMapping, 68 pub(crate) candidate_type: CandidateType, 69 } 70 71 impl ExternalIpMapper { 72 pub(crate) fn new(mut candidate_type: CandidateType, ips: &[String]) -> Result<Option<Self>> { 73 if ips.is_empty() { 74 return Ok(None); 75 } 76 if candidate_type == CandidateType::Unspecified { 77 candidate_type = CandidateType::Host; // defaults to host 78 } else if candidate_type != CandidateType::Host 79 && candidate_type != CandidateType::ServerReflexive 80 { 81 return Err(Error::ErrUnsupportedNat1to1IpCandidateType); 82 } 83 84 let mut m = Self { 85 ipv4_mapping: IpMapping::default(), 86 ipv6_mapping: IpMapping::default(), 87 candidate_type, 88 }; 89 90 for ext_ip_str in ips { 91 let ip_pair: Vec<&str> = ext_ip_str.split('/').collect(); 92 if ip_pair.is_empty() || ip_pair.len() > 2 { 93 return Err(Error::ErrInvalidNat1to1IpMapping); 94 } 95 96 let ext_ip = validate_ip_string(ip_pair[0])?; 97 if ip_pair.len() == 1 { 98 if ext_ip.is_ipv4() { 99 m.ipv4_mapping.set_sole_ip(ext_ip)?; 100 } else { 101 m.ipv6_mapping.set_sole_ip(ext_ip)?; 102 } 103 } else { 104 let loc_ip = validate_ip_string(ip_pair[1])?; 105 if ext_ip.is_ipv4() { 106 if !loc_ip.is_ipv4() { 107 return Err(Error::ErrInvalidNat1to1IpMapping); 108 } 109 110 m.ipv4_mapping.add_ip_mapping(loc_ip, ext_ip)?; 111 } else { 112 if loc_ip.is_ipv4() { 113 return Err(Error::ErrInvalidNat1to1IpMapping); 114 } 115 116 m.ipv6_mapping.add_ip_mapping(loc_ip, ext_ip)?; 117 } 118 } 119 } 120 121 Ok(Some(m)) 122 } 123 124 pub(crate) fn find_external_ip(&self, local_ip_str: &str) -> Result<IpAddr> { 125 let loc_ip = validate_ip_string(local_ip_str)?; 126 127 if loc_ip.is_ipv4() { 128 self.ipv4_mapping.find_external_ip(loc_ip) 129 } else { 130 self.ipv6_mapping.find_external_ip(loc_ip) 131 } 132 } 133 } 134