webrtc_ice/external_ip_mapper/
mod.rs

1#[cfg(test)]
2mod external_ip_mapper_test;
3
4use std::collections::HashMap;
5use std::net::IpAddr;
6
7use crate::candidate::*;
8use crate::error::*;
9
10pub(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)]
19pub(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
24impl 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)]
65pub(crate) struct ExternalIpMapper {
66    pub(crate) ipv4_mapping: IpMapping,
67    pub(crate) ipv6_mapping: IpMapping,
68    pub(crate) candidate_type: CandidateType,
69}
70
71impl 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}