webrtc_ice/external_ip_mapper/
mod.rs1#[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#[derive(Default, PartialEq, Debug)]
19pub(crate) struct IpMapping {
20 ip_sole: Option<IpAddr>, ip_map: HashMap<String, IpAddr>, }
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 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; } 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}