igd/
errors.rs

1use std::error;
2use std::fmt;
3use std::io;
4use std::str;
5#[cfg(feature = "aio")]
6use std::string::FromUtf8Error;
7
8#[cfg(feature = "aio")]
9use tokio::time::error::Elapsed;
10
11/// Errors that can occur when sending the request to the gateway.
12#[derive(Debug)]
13pub enum RequestError {
14    /// attohttp error
15    AttoHttpError(attohttpc::Error),
16    /// IO Error
17    IoError(io::Error),
18    /// The response from the gateway could not be parsed.
19    InvalidResponse(String),
20    /// The gateway returned an unhandled error code and description.
21    ErrorCode(u16, String),
22    /// Action is not supported by the gateway
23    UnsupportedAction(String),
24    /// When using the aio feature.
25    #[cfg(feature = "aio")]
26    HyperError(hyper::Error),
27
28    #[cfg(feature = "aio")]
29    /// http crate error type
30    HttpError(http::Error),
31
32    #[cfg(feature = "aio")]
33    /// Error parsing HTTP body
34    Utf8Error(FromUtf8Error),
35}
36
37impl From<attohttpc::Error> for RequestError {
38    fn from(err: attohttpc::Error) -> RequestError {
39        RequestError::AttoHttpError(err)
40    }
41}
42
43impl From<io::Error> for RequestError {
44    fn from(err: io::Error) -> RequestError {
45        RequestError::IoError(err)
46    }
47}
48
49#[cfg(feature = "aio")]
50impl From<http::Error> for RequestError {
51    fn from(err: http::Error) -> RequestError {
52        RequestError::HttpError(err)
53    }
54}
55
56#[cfg(feature = "aio")]
57impl From<hyper::Error> for RequestError {
58    fn from(err: hyper::Error) -> RequestError {
59        RequestError::HyperError(err)
60    }
61}
62
63#[cfg(feature = "aio")]
64impl From<FromUtf8Error> for RequestError {
65    fn from(err: FromUtf8Error) -> RequestError {
66        RequestError::Utf8Error(err)
67    }
68}
69
70#[cfg(feature = "aio")]
71impl From<Elapsed> for RequestError {
72    fn from(_err: Elapsed) -> RequestError {
73        RequestError::IoError(io::Error::new(io::ErrorKind::TimedOut, "timer failed"))
74    }
75}
76
77impl fmt::Display for RequestError {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        match *self {
80            RequestError::AttoHttpError(ref e) => write!(f, "HTTP error {}", e),
81            RequestError::InvalidResponse(ref e) => write!(f, "Invalid response from gateway: {}", e),
82            RequestError::IoError(ref e) => write!(f, "IO error. {}", e),
83            RequestError::ErrorCode(n, ref e) => write!(f, "Gateway response error {}: {}", n, e),
84            RequestError::UnsupportedAction(ref e) => write!(f, "Gateway does not support action: {}", e),
85            #[cfg(feature = "aio")]
86            RequestError::HyperError(ref e) => write!(f, "Hyper Error: {}", e),
87            #[cfg(feature = "aio")]
88            RequestError::HttpError(ref e) => write!(f, "Http  Error: {}", e),
89            #[cfg(feature = "aio")]
90            RequestError::Utf8Error(ref e) => write!(f, "Utf8Error Error: {}", e),
91        }
92    }
93}
94
95impl std::error::Error for RequestError {
96    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
97        match *self {
98            RequestError::AttoHttpError(ref e) => Some(e),
99            RequestError::InvalidResponse(..) => None,
100            RequestError::IoError(ref e) => Some(e),
101            RequestError::ErrorCode(..) => None,
102            RequestError::UnsupportedAction(..) => None,
103            #[cfg(feature = "aio")]
104            RequestError::HyperError(ref e) => Some(e),
105            #[cfg(feature = "aio")]
106            RequestError::HttpError(ref e) => Some(e),
107            #[cfg(feature = "aio")]
108            RequestError::Utf8Error(ref e) => Some(e),
109        }
110    }
111}
112
113/// Errors returned by `Gateway::get_external_ip`
114#[derive(Debug)]
115pub enum GetExternalIpError {
116    /// The client is not authorized to perform the operation.
117    ActionNotAuthorized,
118    /// Some other error occured performing the request.
119    RequestError(RequestError),
120}
121
122/// Errors returned by `Gateway::remove_port`
123#[derive(Debug)]
124pub enum RemovePortError {
125    /// The client is not authorized to perform the operation.
126    ActionNotAuthorized,
127    /// No such port mapping.
128    NoSuchPortMapping,
129    /// Some other error occured performing the request.
130    RequestError(RequestError),
131}
132
133/// Errors returned by `Gateway::add_any_port` and `Gateway::get_any_address`
134#[derive(Debug)]
135pub enum AddAnyPortError {
136    /// The client is not authorized to perform the operation.
137    ActionNotAuthorized,
138    /// Can not add a mapping for local port 0.
139    InternalPortZeroInvalid,
140    /// The gateway does not have any free ports.
141    NoPortsAvailable,
142    /// The gateway can only map internal ports to same-numbered external ports
143    /// and this external port is in use.
144    ExternalPortInUse,
145    /// The gateway only supports permanent leases (ie. a `lease_duration` of 0).
146    OnlyPermanentLeasesSupported,
147    /// The description was too long for the gateway to handle.
148    DescriptionTooLong,
149    /// Some other error occured performing the request.
150    RequestError(RequestError),
151}
152
153impl From<RequestError> for AddAnyPortError {
154    fn from(err: RequestError) -> AddAnyPortError {
155        AddAnyPortError::RequestError(err)
156    }
157}
158
159impl From<GetExternalIpError> for AddAnyPortError {
160    fn from(err: GetExternalIpError) -> AddAnyPortError {
161        match err {
162            GetExternalIpError::ActionNotAuthorized => AddAnyPortError::ActionNotAuthorized,
163            GetExternalIpError::RequestError(e) => AddAnyPortError::RequestError(e),
164        }
165    }
166}
167
168/// Errors returned by `Gateway::add_port`
169#[derive(Debug)]
170pub enum AddPortError {
171    /// The client is not authorized to perform the operation.
172    ActionNotAuthorized,
173    /// Can not add a mapping for local port 0.
174    InternalPortZeroInvalid,
175    /// External port number 0 (any port) is considered invalid by the gateway.
176    ExternalPortZeroInvalid,
177    /// The requested mapping conflicts with a mapping assigned to another client.
178    PortInUse,
179    /// The gateway requires that the requested internal and external ports are the same.
180    SamePortValuesRequired,
181    /// The gateway only supports permanent leases (ie. a `lease_duration` of 0).
182    OnlyPermanentLeasesSupported,
183    /// The description was too long for the gateway to handle.
184    DescriptionTooLong,
185    /// Some other error occured performing the request.
186    RequestError(RequestError),
187}
188
189impl fmt::Display for GetExternalIpError {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        match *self {
192            GetExternalIpError::ActionNotAuthorized => write!(f, "The client is not authorized to remove the port"),
193            GetExternalIpError::RequestError(ref e) => write!(f, "Request Error. {}", e),
194        }
195    }
196}
197
198impl From<io::Error> for GetExternalIpError {
199    fn from(err: io::Error) -> GetExternalIpError {
200        GetExternalIpError::RequestError(RequestError::from(err))
201    }
202}
203
204impl std::error::Error for GetExternalIpError {
205    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
206        None
207    }
208}
209
210impl fmt::Display for RemovePortError {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        match *self {
213            RemovePortError::ActionNotAuthorized => write!(f, "The client is not authorized to remove the port"),
214            RemovePortError::NoSuchPortMapping => write!(f, "The port was not mapped"),
215            RemovePortError::RequestError(ref e) => write!(f, "Request error. {}", e),
216        }
217    }
218}
219
220impl std::error::Error for RemovePortError {
221    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
222        None
223    }
224}
225
226impl fmt::Display for AddAnyPortError {
227    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228        match *self {
229            AddAnyPortError::ActionNotAuthorized => {
230                write!(f, "The client is not authorized to remove the port")
231            }
232            AddAnyPortError::InternalPortZeroInvalid => {
233                write!(f, "Can not add a mapping for local port 0")
234            }
235            AddAnyPortError::NoPortsAvailable => {
236                write!(f, "The gateway does not have any free ports")
237            }
238            AddAnyPortError::OnlyPermanentLeasesSupported => {
239                write!(
240                    f,
241                    "The gateway only supports permanent leases (ie. a `lease_duration` of 0),"
242                )
243            }
244            AddAnyPortError::ExternalPortInUse => {
245                write!(
246                    f,
247                    "The gateway can only map internal ports to same-numbered external ports and this external port is in use."
248                )
249            }
250            AddAnyPortError::DescriptionTooLong => {
251                write!(f, "The description was too long for the gateway to handle.")
252            }
253            AddAnyPortError::RequestError(ref e) => write!(f, "Request error. {}", e),
254        }
255    }
256}
257
258impl std::error::Error for AddAnyPortError {
259    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
260        None
261    }
262}
263
264impl fmt::Display for AddPortError {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        match *self {
267            AddPortError::ActionNotAuthorized => write!(f, "The client is not authorized to map this port."),
268            AddPortError::InternalPortZeroInvalid => write!(f, "Can not add a mapping for local port 0"),
269            AddPortError::ExternalPortZeroInvalid => write!(
270                f,
271                "External port number 0 (any port) is considered invalid by the gateway."
272            ),
273            AddPortError::PortInUse => write!(
274                f,
275                "The requested mapping conflicts with a mapping assigned to another client."
276            ),
277            AddPortError::SamePortValuesRequired => write!(
278                f,
279                "The gateway requires that the requested internal and external ports are the same."
280            ),
281            AddPortError::OnlyPermanentLeasesSupported => write!(
282                f,
283                "The gateway only supports permanent leases (ie. a `lease_duration` of 0),"
284            ),
285            AddPortError::DescriptionTooLong => write!(f, "The description was too long for the gateway to handle."),
286            AddPortError::RequestError(ref e) => write!(f, "Request error. {}", e),
287        }
288    }
289}
290
291impl std::error::Error for AddPortError {
292    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
293        None
294    }
295}
296
297/// Errors than can occur while trying to find the gateway.
298#[derive(Debug)]
299pub enum SearchError {
300    /// Http/Hyper error
301    HttpError(attohttpc::Error),
302    /// Unable to process the response
303    InvalidResponse,
304    /// IO Error
305    IoError(io::Error),
306    /// UTF-8 decoding error
307    Utf8Error(str::Utf8Error),
308    /// XML processing error
309    XmlError(xmltree::ParseError),
310    /// When using the aio feature.
311    #[cfg(feature = "aio")]
312    HyperError(hyper::Error),
313    /// Error parsing URI
314    #[cfg(feature = "aio")]
315    InvalidUri(hyper::http::uri::InvalidUri),
316}
317
318impl From<attohttpc::Error> for SearchError {
319    fn from(err: attohttpc::Error) -> SearchError {
320        SearchError::HttpError(err)
321    }
322}
323
324impl From<io::Error> for SearchError {
325    fn from(err: io::Error) -> SearchError {
326        SearchError::IoError(err)
327    }
328}
329
330impl From<str::Utf8Error> for SearchError {
331    fn from(err: str::Utf8Error) -> SearchError {
332        SearchError::Utf8Error(err)
333    }
334}
335
336impl From<xmltree::ParseError> for SearchError {
337    fn from(err: xmltree::ParseError) -> SearchError {
338        SearchError::XmlError(err)
339    }
340}
341
342#[cfg(feature = "aio")]
343impl From<hyper::Error> for SearchError {
344    fn from(err: hyper::Error) -> SearchError {
345        SearchError::HyperError(err)
346    }
347}
348
349#[cfg(feature = "aio")]
350impl From<hyper::http::uri::InvalidUri> for SearchError {
351    fn from(err: hyper::http::uri::InvalidUri) -> SearchError {
352        SearchError::InvalidUri(err)
353    }
354}
355#[cfg(feature = "aio")]
356impl From<Elapsed> for SearchError {
357    fn from(_err: Elapsed) -> SearchError {
358        SearchError::IoError(io::Error::new(io::ErrorKind::TimedOut, "search timed out"))
359    }
360}
361
362impl fmt::Display for SearchError {
363    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
364        match *self {
365            SearchError::HttpError(ref e) => write!(f, "HTTP error {}", e),
366            SearchError::InvalidResponse => write!(f, "Invalid response"),
367            SearchError::IoError(ref e) => write!(f, "IO error: {}", e),
368            SearchError::Utf8Error(ref e) => write!(f, "UTF-8 error: {}", e),
369            SearchError::XmlError(ref e) => write!(f, "XML error: {}", e),
370            #[cfg(feature = "aio")]
371            SearchError::HyperError(ref e) => write!(f, "Hyper Error: {}", e),
372            #[cfg(feature = "aio")]
373            SearchError::InvalidUri(ref e) => write!(f, "InvalidUri Error: {}", e),
374        }
375    }
376}
377
378impl error::Error for SearchError {
379    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
380        match *self {
381            SearchError::HttpError(ref e) => Some(e),
382            SearchError::InvalidResponse => None,
383            SearchError::IoError(ref e) => Some(e),
384            SearchError::Utf8Error(ref e) => Some(e),
385            SearchError::XmlError(ref e) => Some(e),
386            #[cfg(feature = "aio")]
387            SearchError::HyperError(ref e) => Some(e),
388            #[cfg(feature = "aio")]
389            SearchError::InvalidUri(ref e) => Some(e),
390        }
391    }
392}
393
394/// Errors than can occur while getting a port mapping
395#[derive(Debug)]
396pub enum GetGenericPortMappingEntryError {
397    /// The client is not authorized to perform the operation.
398    ActionNotAuthorized,
399    /// The specified array index is out of bounds.
400    SpecifiedArrayIndexInvalid,
401    /// Some other error occured performing the request.
402    RequestError(RequestError),
403}
404
405impl From<RequestError> for GetGenericPortMappingEntryError {
406    fn from(err: RequestError) -> GetGenericPortMappingEntryError {
407        match err {
408            RequestError::ErrorCode(code, _) if code == 606 => GetGenericPortMappingEntryError::ActionNotAuthorized,
409            RequestError::ErrorCode(code, _) if code == 713 => {
410                GetGenericPortMappingEntryError::SpecifiedArrayIndexInvalid
411            }
412            other => GetGenericPortMappingEntryError::RequestError(other),
413        }
414    }
415}
416
417impl fmt::Display for GetGenericPortMappingEntryError {
418    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
419        match *self {
420            GetGenericPortMappingEntryError::ActionNotAuthorized => {
421                write!(f, "The client is not authorized to look up port mappings.")
422            }
423            GetGenericPortMappingEntryError::SpecifiedArrayIndexInvalid => {
424                write!(f, "The provided index into the port mapping list is invalid.")
425            }
426            GetGenericPortMappingEntryError::RequestError(ref e) => e.fmt(f),
427        }
428    }
429}
430
431impl std::error::Error for GetGenericPortMappingEntryError {}
432
433/// An error type that emcompasses all possible errors.
434#[derive(Debug)]
435pub enum Error {
436    /// `AddAnyPortError`
437    AddAnyPortError(AddAnyPortError),
438    /// `AddPortError`
439    AddPortError(AddPortError),
440    /// `GetExternalIpError`
441    GetExternalIpError(GetExternalIpError),
442    /// `RemovePortError`
443    RemovePortError(RemovePortError),
444    /// `RequestError`
445    RequestError(RequestError),
446    /// `SearchError`
447    SearchError(SearchError),
448}
449
450/// A result type where the error is `igd::Error`.
451pub type Result<T = ()> = std::result::Result<T, Error>;
452
453impl fmt::Display for Error {
454    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
455        match *self {
456            Error::AddAnyPortError(ref e) => e.fmt(f),
457            Error::AddPortError(ref e) => e.fmt(f),
458            Error::GetExternalIpError(ref e) => e.fmt(f),
459            Error::RemovePortError(ref e) => e.fmt(f),
460            Error::RequestError(ref e) => e.fmt(f),
461            Error::SearchError(ref e) => e.fmt(f),
462        }
463    }
464}
465
466impl error::Error for Error {
467    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
468        match *self {
469            Error::AddAnyPortError(ref e) => Some(e),
470            Error::AddPortError(ref e) => Some(e),
471            Error::GetExternalIpError(ref e) => Some(e),
472            Error::RemovePortError(ref e) => Some(e),
473            Error::RequestError(ref e) => Some(e),
474            Error::SearchError(ref e) => Some(e),
475        }
476    }
477}
478
479impl From<AddAnyPortError> for Error {
480    fn from(err: AddAnyPortError) -> Error {
481        Error::AddAnyPortError(err)
482    }
483}
484
485impl From<AddPortError> for Error {
486    fn from(err: AddPortError) -> Error {
487        Error::AddPortError(err)
488    }
489}
490
491impl From<GetExternalIpError> for Error {
492    fn from(err: GetExternalIpError) -> Error {
493        Error::GetExternalIpError(err)
494    }
495}
496
497impl From<RemovePortError> for Error {
498    fn from(err: RemovePortError) -> Error {
499        Error::RemovePortError(err)
500    }
501}
502
503impl From<RequestError> for Error {
504    fn from(err: RequestError) -> Error {
505        Error::RequestError(err)
506    }
507}
508
509impl From<SearchError> for Error {
510    fn from(err: SearchError) -> Error {
511        Error::SearchError(err)
512    }
513}