xref: /webrtc/ice/src/external_ip_mapper/mod.rs (revision ffe74184)
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 
validate_ip_string(ip_str: &str) -> Result<IpAddr>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 {
set_sole_ip(&mut self, ip: IpAddr) -> Result<()>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 
add_ip_mapping(&mut self, loc_ip: IpAddr, ext_ip: IpAddr) -> Result<()>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 
find_external_ip(&self, loc_ip: IpAddr) -> Result<IpAddr>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 {
new(mut candidate_type: CandidateType, ips: &[String]) -> Result<Option<Self>>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 
find_external_ip(&self, local_ip_str: &str) -> Result<IpAddr>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