1use std::collections::HashMap;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::net::{IpAddr, SocketAddr};
5
6use super::Provider;
7use crate::errors::{self, AddAnyPortError, AddPortError, GetExternalIpError, RemovePortError, RequestError};
8
9use crate::common::{self, messages, parsing, parsing::RequestReponse};
10use crate::PortMappingProtocol;
11
12#[derive(Clone, Debug)]
14pub struct Gateway<P> {
15 pub addr: SocketAddr,
17 pub root_url: String,
19 pub control_url: String,
21 pub control_schema_url: String,
23 pub control_schema: HashMap<String, Vec<String>>,
25 pub provider: P,
27}
28
29impl<P: Provider> Gateway<P> {
30 async fn perform_request(&self, header: &str, body: &str, ok: &str) -> Result<RequestReponse, RequestError> {
31 let url = format!("{self}");
32 let text = P::send_async(&url, header, body).await?;
33 parsing::parse_response(text, ok)
34 }
35
36 pub async fn get_external_ip(&self) -> Result<IpAddr, GetExternalIpError> {
38 let result = self
39 .perform_request(
40 messages::GET_EXTERNAL_IP_HEADER,
41 &messages::format_get_external_ip_message(),
42 "GetExternalIPAddressResponse",
43 )
44 .await;
45 parsing::parse_get_external_ip_response(result)
46 }
47
48 pub async fn get_any_address(
58 &self,
59 protocol: PortMappingProtocol,
60 local_addr: SocketAddr,
61 lease_duration: u32,
62 description: &str,
63 ) -> Result<SocketAddr, AddAnyPortError> {
64 let description = description.to_owned();
65 let ip = self.get_external_ip().await?;
66 let port = self
67 .add_any_port(protocol, local_addr, lease_duration, &description)
68 .await?;
69 Ok(SocketAddr::new(ip, port))
70 }
71
72 pub async fn add_any_port(
81 &self,
82 protocol: PortMappingProtocol,
83 local_addr: SocketAddr,
84 lease_duration: u32,
85 description: &str,
86 ) -> Result<u16, AddAnyPortError> {
87 if local_addr.port() == 0 {
94 return Err(AddAnyPortError::InternalPortZeroInvalid);
95 }
96
97 let schema = self.control_schema.get("AddAnyPortMapping");
98 if let Some(schema) = schema {
99 let external_port = common::random_port();
100
101 let description = description.to_owned();
102
103 let resp = self
104 .perform_request(
105 messages::ADD_ANY_PORT_MAPPING_HEADER,
106 &messages::format_add_any_port_mapping_message(
107 schema,
108 protocol,
109 external_port,
110 local_addr,
111 lease_duration,
112 &description,
113 ),
114 "AddAnyPortMappingResponse",
115 )
116 .await;
117 parsing::parse_add_any_port_mapping_response(resp)
118 } else {
119 self.retry_add_random_port_mapping(protocol, local_addr, lease_duration, description)
122 .await
123 }
124 }
125
126 async fn retry_add_random_port_mapping(
127 &self,
128 protocol: PortMappingProtocol,
129 local_addr: SocketAddr,
130 lease_duration: u32,
131 description: &str,
132 ) -> Result<u16, AddAnyPortError> {
133 for _ in 0u8..20u8 {
134 match self
135 .add_random_port_mapping(protocol, local_addr, lease_duration, description)
136 .await
137 {
138 Ok(port) => return Ok(port),
139 Err(AddAnyPortError::NoPortsAvailable) => continue,
140 e => return e,
141 }
142 }
143 Err(AddAnyPortError::NoPortsAvailable)
144 }
145
146 async fn add_random_port_mapping(
147 &self,
148 protocol: PortMappingProtocol,
149 local_addr: SocketAddr,
150 lease_duration: u32,
151 description: &str,
152 ) -> Result<u16, AddAnyPortError> {
153 let description = description.to_owned();
154 let external_port = common::random_port();
155 let res = self
156 .add_port_mapping(protocol, external_port, local_addr, lease_duration, &description)
157 .await;
158
159 match res {
160 Ok(_) => Ok(external_port),
161 Err(err) => match parsing::convert_add_random_port_mapping_error(err) {
162 Some(err) => Err(err),
163 None => {
164 self.add_same_port_mapping(protocol, local_addr, lease_duration, &description)
165 .await
166 }
167 },
168 }
169 }
170
171 async fn add_same_port_mapping(
172 &self,
173 protocol: PortMappingProtocol,
174 local_addr: SocketAddr,
175 lease_duration: u32,
176 description: &str,
177 ) -> Result<u16, AddAnyPortError> {
178 let res = self
179 .add_port_mapping(protocol, local_addr.port(), local_addr, lease_duration, description)
180 .await;
181 match res {
182 Ok(_) => Ok(local_addr.port()),
183 Err(err) => Err(parsing::convert_add_same_port_mapping_error(err)),
184 }
185 }
186
187 async fn add_port_mapping(
188 &self,
189 protocol: PortMappingProtocol,
190 external_port: u16,
191 local_addr: SocketAddr,
192 lease_duration: u32,
193 description: &str,
194 ) -> Result<(), RequestError> {
195 self.perform_request(
196 messages::ADD_PORT_MAPPING_HEADER,
197 &messages::format_add_port_mapping_message(
198 self.control_schema
199 .get("AddPortMapping")
200 .ok_or_else(|| RequestError::UnsupportedAction("AddPortMapping".to_string()))?,
201 protocol,
202 external_port,
203 local_addr,
204 lease_duration,
205 description,
206 ),
207 "AddPortMappingResponse",
208 )
209 .await?;
210 Ok(())
211 }
212
213 pub async fn add_port(
218 &self,
219 protocol: PortMappingProtocol,
220 external_port: u16,
221 local_addr: SocketAddr,
222 lease_duration: u32,
223 description: &str,
224 ) -> Result<(), AddPortError> {
225 if external_port == 0 {
226 return Err(AddPortError::ExternalPortZeroInvalid);
227 }
228 if local_addr.port() == 0 {
229 return Err(AddPortError::InternalPortZeroInvalid);
230 }
231
232 let res = self
233 .add_port_mapping(protocol, external_port, local_addr, lease_duration, description)
234 .await;
235 if let Err(err) = res {
236 return Err(parsing::convert_add_port_error(err));
237 };
238 Ok(())
239 }
240
241 pub async fn remove_port(&self, protocol: PortMappingProtocol, external_port: u16) -> Result<(), RemovePortError> {
243 let res = self
244 .perform_request(
245 messages::DELETE_PORT_MAPPING_HEADER,
246 &messages::format_delete_port_message(
247 self.control_schema.get("DeletePortMapping").ok_or_else(|| {
248 RemovePortError::RequestError(RequestError::UnsupportedAction("DeletePortMapping".to_string()))
249 })?,
250 protocol,
251 external_port,
252 ),
253 "DeletePortMappingResponse",
254 )
255 .await;
256 parsing::parse_delete_port_mapping_response(res)
257 }
258
259 pub async fn get_generic_port_mapping_entry(
265 &self,
266 index: u32,
267 ) -> Result<parsing::PortMappingEntry, errors::GetGenericPortMappingEntryError> {
268 let result = self
269 .perform_request(
270 messages::GET_GENERIC_PORT_MAPPING_ENTRY,
271 &messages::formate_get_generic_port_mapping_entry_message(index),
272 "GetGenericPortMappingEntryResponse",
273 )
274 .await;
275 parsing::parse_get_generic_port_mapping_entry(result)
276 }
277}
278
279impl<P> fmt::Display for Gateway<P> {
280 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281 write!(f, "http://{}{}", self.addr, self.control_url)
282 }
283}
284
285impl<P> PartialEq for Gateway<P> {
286 fn eq(&self, other: &Gateway<P>) -> bool {
287 self.addr == other.addr && self.control_url == other.control_url
288 }
289}
290
291impl<P> Eq for Gateway<P> {}
292
293impl<P> Hash for Gateway<P> {
294 fn hash<H: Hasher>(&self, state: &mut H) {
295 self.addr.hash(state);
296 self.control_url.hash(state);
297 }
298}